UDP 전송 패 킷 프로 세 스
18099 단어 네트워크프로 토 콜 스 택개인 노트udp
(1), kiocb: 사용자 주소 공간 작업 효율 을 높이 기 위 한 데이터 구조 체.
(2), sk: 열 린 소켓 데이터 구 조 는 소켓 의 모든 설정 정보 와 옵션 을 포함 합 니 다.
(3), msg: 사용자 주소 공간 을 관리 하 는 데이터 구 조 를 저장 합 니 다.
(4), len: 사용자 공간 에서 받 아들 인 패 킷 길이.
int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t len)
struct msghdr 구조 체:
struct msghdr {
void * msg_name; /* Socket name */
int msg_namelen; /* Length of name */
struct iovec * msg_iov; /* Data blocks */
__kernel_size_t msg_iovlen; /* Number of blocks */
void * msg_control; /* Per protocol magic (eg BSD file descriptor passing) */
__kernel_size_t msg_controllen; /* Length of cmsg list */
unsigned msg_flags; //
};
1. 정확성 검사
먼저 패 킷 의 정확 한 검 사 를 하 는 것 입 니 다. 만약 에 데이터 포인터 가 메모리 에 있 을 수록 운영 체제 가 붕 괴 될 수 있 기 때문에 데이터 의 정확성 검 사 를 해 야 합 니 다. 먼저 패 킷 의 길이 가 0xFFFF 보다 작 는 지 확인 해 야 합 니 다. udp 패 킷 의 길 이 는 최대 16 비트 를 표시 한 다음 에 소켓 표지 가 불법 MSG 를 설정 하 는 지 확인 해 야 합 니 다.OOB (데이터 외부 전송 허용)
...
//
if (len > 0xFFFF)
return -EMSGSIZE;
/*
* Check the flags.
*/
//
if (msg->msg_flags & MSG_OOB) /* Mirror BSD error message compatibility */
return -EOPNOTSUPP;
...
2. 초기 에 걸 려 있 는 데 이 터 를 처리 합 니 다.
현재 소켓 에 전송 대기 데이터 가 걸 려 있 는 지 확인 하고 있 으 면 do 로 이동 합 니 다.append_data 탭 에서 보 내 기 를 기다 리 는 패 키 지 를 먼저 처리 합 니 다.
...
//
if (up->pending) {
/*
* There are pending frames.
* The socket lock must be held while it's corked.
*/
lock_sock(sk);
if (likely(up->pending)) {
// AF_INET
if (unlikely(up->pending != AF_INET)) {
release_sock(sk);
return -EINVAL;
}
// IP
goto do_append_data;
}
release_sock(sk);
}
...
3. 새로운 데이터 처리
보 내 기 를 기다 리 는 패 킷 이 걸 리 지 않 았 다 면, udpsendmsg 는 사용자 공간 에서 전 송 된 데 이 터 를 처리 합 니 다.새로운 데이터 처 리 는 주로 세 가지 측면 이 있 는데 그것 이 바로 목적 IP 주소 검사, 연결 여 부 를 판단 하고 정보 처 리 를 제어 하 는 것 이다.
(1) 목적 IP 검사
목적 Ip 주소 msg - > msgname 이 비어 있 지 않 으 면 목적 ip 주 소 를 검사 해 야 합 니 다. 목적 ip 주 소 는 소켓 이름 으로 제공 되 고 소켓 이름 은 msg - > msg 에 저 장 됩 니 다.name 데이터 필드, 우선 목적 Ip 주소 의 길이 와 프로 토 콜 족 이 AF 인지 확인 합 니 다.INET, 그렇지 않 으 면 오 류 를 되 돌려 줍 니 다. 검 사 를 통과 한 후 목적 ip 과 목적 포트 를 부분 변수 daddr, dport 에 할당 합 니 다.
...
// IP
if (msg->msg_name) {
struct sockaddr_in * usin = (struct sockaddr_in *)msg->msg_name;
//
if (msg->msg_namelen < sizeof(*usin))
return -EINVAL;
//
if (usin->sin_family != AF_INET) {
if (usin->sin_family != AF_UNSPEC)
return -EAFNOSUPPORT;
}
// ip
daddr = usin->sin_addr.s_addr;
dport = usin->sin_port;
if (dport == 0)
return -EINVAL;
} else {
...
(2) 、 연결 되 었 습 니 다
msg->msg_name 의 목적 주소 가 비어 있 을 때 연결 상 태 를 확인 하고 sk - > skstate 는 TCP 와 같 습 니까?ESTABLISHED, 그렇지 않 으 면 잘못된 목적 주 소 를 되 돌려 줍 니 다. sk - > skstate 는 TCPESTABLISHED 는 목적 경로 가 고속 레지스터 에 저장 되 어 있다 는 것 을 설명 합 니 다. 이 때 응용 층 이 들 어 오 는 목적 ip 이 비어 있어 도 패 킷 을 보 낼 수 있 습 니 다. Inet 에서 목적 ip 과 목적 포트 를 꺼 내 국부 변수 daddr, dport 에 할당 할 수 있 습 니 다.
...
// IP TCP_ESTABLISHED
if (sk->sk_state != TCP_ESTABLISHED)
return -EDESTADDRREQ;
// ip
daddr = inet->inet_daddr;
dport = inet->inet_dport;
/* Open fast path for connected socket.
Route will not be used, if at least one option is set.
*/
connected = 1;
...
(3) 제어 정보 처리
처리 및 목적 IP 후 udp 제어 정 보 를 처리 해 야 합 니 다. udp 제어 정 보 는 msg - > msg 에 저 장 됩 니 다.control 데이터 필드 에서 msg - > msgcontrolen 0 이 아 닌 제어 정보 가 있 음 을 설명 하고 제어 정 보 는 함수 ipcmsg_send 해석 은 부분 변수 struct ipcm 에 저 장 됩 니 다.cookie ipc 중.
struct ipcm_cookie{
__be32 addr; //
int oif //
struct ip_option *opt; //ip
}
ip_cmsg_send 처리 udp 의 제어 정 보 는 주로 두 가지 가 있 습 니 다.
a、IP_RETOPTS: ip 프로 토 콜 헤더 에서 ip 옵션 을 가 져 와 ipc - > opt 에 저장 합 니 다.
b、IP_PKTInfo: 네트워크 장치 의 색인 과 ip 주 소 를 ipc - > addr, ipc - > oif 로 되 돌려 줍 니 다.
int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc)
{
int err;
struct cmsghdr *cmsg;
for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
if (!CMSG_OK(msg, cmsg))
return -EINVAL;
if (cmsg->cmsg_level != SOL_IP)
continue;
switch (cmsg->cmsg_type) {
//ip
case IP_RETOPTS:
err = cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr));
// ip Ip
err = ip_options_get(net, &ipc->opt, CMSG_DATA(cmsg),
err < 40 ? err : 40);
if (err)
return err;
break;
// ip
case IP_PKTINFO:
{
struct in_pktinfo *info;
if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct in_pktinfo)))
return -EINVAL;
info = (struct in_pktinfo *)CMSG_DATA(cmsg);
//
ipc->oif = info->ipi_ifindex;
//
ipc->addr = info->ipi_spec_dst.s_addr;
break;
}
default:
return -EINVAL;
}
}
return 0;
}
소켓 에 ip 옵션 이 설정 되 어 있 지 않 으 면 inet 에서 ip 옵션 데이터 필드 를 가 져 옵 니 다.
...
//
if (msg->msg_controllen) {
// ipc
err = ip_cmsg_send(sock_net(sk), msg, &ipc);
if (err)
return err;
if (ipc.opt)
free = 1;
connected = 0;
}
//
// inet ip
if (!ipc.opt)
ipc.opt = inet->opt;
// saddr
saddr = ipc.addr;
// ipc.addr
ipc.addr = faddr = daddr;
...
4. 경로 판단
ip 층 에 데 이 터 를 보 내 려 면 먼저 경로, 경로 의 상황 을 판단 해 야 합 니 다.
4.1 루트 설정 필요 없 음
경로 설정 이 필요 없 는 것 은 주로 네 가지 상황 이 있 습 니 다.
a. 패 킷 은 로 컬 랜 전송 표지 로 sk - > localrroute 입 니 다.
b 、 입력 정보 옵션 msg - > msgflags 는 경로 표시 가 필요 하지 않 습 니 다.
c. ip 옵션 은 엄격 한 경로 옵션 을 설정 합 니 다.
d. 목적 주 소 는 그룹 주소 이 고 경로 도 설정 하지 마 십시오.
...
tos = RT_TOS(inet->tos);
// SOCK_LOCALROUTE
// msg_flags MSG_DONTROUTE
// , tos RTO+ONLINK
if (sock_flag(sk, SOCK_LOCALROUTE) ||
(msg->msg_flags & MSG_DONTROUTE) ||
(ipc.opt && ipc.opt->is_strictroute)) {
tos |= RTO_ONLINK;
connected = 0;
}
//
if (ipv4_is_multicast(daddr)) {
if (!ipc.oif)
ipc.oif = inet->mc_index;
if (!saddr)
saddr = inet->mc_addr;
connected = 0;
}
...
4.2 경로 가 이미 알 고 있다.
경로 가 알려 진 것 은 connected 플래그 비트 1 입 니 다. 경로 가 고속 버퍼 레지스터 에서 부분 변수 rt 에 저 장 됩 니 다.
...
// , rt
if (connected)
rt = (struct rtable *)sk_dst_check(sk, 0);
...
4.3 목적 경로 가 유효 하지 않 음
목적 경로 가 rt 가 NULL 인지 확인 하면 ip 를 호출 합 니 다.route_output_flow 함 수 는 루트 테이블 에서 루트 를 검색 하려 면 루트 를 검색 하려 면 먼저 struct flowing 구조 체 를 구축 해 야 한다. 주로 IP, 목적 IP, 소스 포트, 목적 포트, 출력 네트워크 장치 의 루트 를 더욱 원활 하 게 해 야 한다.가 져 온 경로 가 라디오 경로 라면 소켓 에 SO 가 설정 되 어 있 지 않 습 니 다.BROADCAST 옵션 을 선택 하면 오 류 를 되 돌려 줍 니 다.
...
//
if (rt == NULL) {
struct flowi fl = { .oif = ipc.oif,
.mark = sk->sk_mark,
.nl_u = { .ip4_u =
{ .daddr = faddr,
.saddr = saddr,
.tos = tos } },
.proto = sk->sk_protocol,
.flags = inet_sk_flowi_flags(sk),
.uli_u = { .ports =
{ .sport = inet->inet_sport,
.dport = dport } } };
struct net *net = sock_net(sk);
security_sk_classify_flow(sk, &fl);
//
err = ip_route_output_flow(net, &rt, &fl, sk, 1);
if (err) {
if (err == -ENETUNREACH)
IP_INC_STATS_BH(net, IPSTATS_MIB_OUTNOROUTES);
goto out;
}
err = -EACCES;
// , SO_BROADCAST
if ((rt->rt_flags & RTCF_BROADCAST) &&
!sock_flag(sk, SOCK_BROADCAST))
goto out;
if (connected)
// , rt
sk_dst_set(sk, dst_clone(&rt->u.dst));
}
...
4.4. 경로 가 이미 알 고 있다.
목적 주소 까지 의 유효한 경로 가 만 들 어 졌 습 니 다. 소켓 옵션 이 MSG 를 설정 하 였 는 지 판단 합 니 다.CONFIRM (소켓 이 유효한 경로 정 보 를 되 돌려 줍 니 다) 설정 하면 경로 정보 처리 탭 으로 이동 합 니 다: doconfirm
...
// MSG_CONFIRM
//
if (msg->msg_flags&MSG_CONFIRM)
goto do_confirm;
...
5. IP 층 에 데이터 전송
ip 층 에 데 이 터 를 보 내 는 것 은 주로 세 단계 로 나 뉜 다.
(1) 자 물 쇠 를 추가 합 니 다. 소켓 이 막 히 면 소켓 을 연결 하고 잘못된 정 보 를 되 돌려 줄 지 여부 입 니 다.
...
lock_sock(sk);
if (unlikely(up->pending)) {
/* The socket is already corked while preparing it. */
/* ... which is an evident application bug. --ANK */
// ,
release_sock(sk);
LIMIT_NETDEBUG(KERN_DEBUG "udp cork app bug 2
");
err = -EINVAL;
goto out;
}
...
(2) 소켓 에 소스 ip, 목적 Ip, 소스 포트, 목적 포트 추가
...
// , IP、 IP、
inet->cork.fl.fl4_dst = daddr;
inet->cork.fl.fl_ip_dport = dport;
inet->cork.fl.fl4_src = saddr;
inet->cork.fl.fl_ip_sport = inet->inet_sport;
up->pending = AF_INET;
...
(3) 、 Ip 호출append_data 는 패 킷 을 IP 계층 버퍼 로 복사 하고 getfrag 는 패 킷 을 응용 층 에서 IP 계층 버퍼 로 복사 합 니 다. 만약 corkreq 가 MSG 를 설정 하지 않 았 다 면.MORE 로고, 그럼 즉시 udp 호출push_pending_frames 는 방금 캐 시 된 패 킷 을 보 냅 니 다.
...
do_append_data:
up->len += ulen;
getfrag = is_udplite ? udplite_getfrag : ip_generic_getfrag;
//
err = ip_append_data(sk, getfrag, msg->msg_iov, ulen,
sizeof(struct udphdr), &ipc, &rt,
corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);
// skb sk_write_queue
if (err)
udp_flush_pending_frames(sk);
//corkreq MSG_MORE udp_push_pending_frames
//
else if (!corkreq)
err = udp_push_pending_frames(sk);
else if (unlikely(skb_queue_empty(&sk->sk_write_queue)))
up->pending = 0;
// sk
release_sock(sk);
...
6、ip_generic_getfrag 함수
ip_generic_getfrag 함수 복 제 는 사용자 공간 에서 커 널 공간 으로 데 이 터 를 복사 합 니 다. 복사 할 때 검사 할 필요 가 없 으 면 memcpy 를 호출 합 니 다.formiovecend, 검사 및 하드웨어 에 남 겨 두 고 검사 가 필요 하 다 면 csum 을 호출 합 니 다.partial_copy_from viovecend 컴 퓨 팅 부분 검사 와 IP 층 으로 복사 합 니 다.udp 검사 와 세 단계 로 나 뉜 다. 데이터 검사 와 udp 프로 토 콜 헤더 검사, ip 프로 토 콜 헤더 검사 와.최종 적 으로 iov 포인터 가 가리 키 는 사용자 주소 공간 에서 커 널 주소 공간의 부분 버퍼 sk 로 데 이 터 를 복사 합 니 다.buff 중.
ip_generic_getfrag 함수:
int
ip_generic_getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb)
{
struct iovec *iov = from;
if (skb->ip_summed == CHECKSUM_PARTIAL) {
//
if (memcpy_fromiovecend(to, iov, offset, len) < 0)
return -EFAULT;
} else {
__wsum csum = 0;
//
if (csum_partial_copy_fromiovecend(to, iov, offset, len, &csum) < 0)
return -EFAULT;
skb->csum = csum_block_add(skb->csum, csum, odd);
}
return 0;
}
struct iov 구조 체
struct iovec
{
void __user *iov_base; /* */
__kernel_size_t iov_len; /* */
};
memcpy_from iovecend 함수:
int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov,
int offset, int len)
{
/* Skip over the finished iovecs */
//
while (offset >= iov->iov_len) {
offset -= iov->iov_len;
iov++;
}
while (len > 0) {
//
u8 __user *base = iov->iov_base + offset;
//iov->iov_len struct iov 、offset
// len iov->iov_len-offset
int copy = min_t(unsigned int, len, iov->iov_len - offset);
offset = 0;
//
if (copy_from_user(kdata, base, copy))
return -EFAULT;
len -= copy;
kdata += copy;
iov++;
}
return 0;
}
udp_sendmsg 함수 코드:
int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t len)
{
struct inet_sock *inet = inet_sk(sk);
struct udp_sock *up = udp_sk(sk);
int ulen = len;
//
struct ipcm_cookie ipc;
//
struct rtable *rt = NULL;
int free = 0;
//
int connected = 0;
__be32 daddr, faddr, saddr;
__be16 dport;
u8 tos;
int err, is_udplite = IS_UDPLITE(sk);
int corkreq = up->corkflag || msg->msg_flags&MSG_MORE;
int (*getfrag)(void *, char *, int, int, int, struct sk_buff *);
//
if (len > 0xFFFF)
return -EMSGSIZE;
/*
* Check the flags.
*/
//
if (msg->msg_flags & MSG_OOB) /* Mirror BSD error message compatibility */
return -EOPNOTSUPP;
ipc.opt = NULL;
ipc.shtx.flags = 0;
//
if (up->pending) {
/*
* There are pending frames.
* The socket lock must be held while it's corked.
*/
lock_sock(sk);
if (likely(up->pending)) {
// AF_INET
if (unlikely(up->pending != AF_INET)) {
release_sock(sk);
return -EINVAL;
}
// IP
goto do_append_data;
}
release_sock(sk);
}
ulen += sizeof(struct udphdr);
/*
* Get and verify the address.
*/
// IP
if (msg->msg_name) {
struct sockaddr_in * usin = (struct sockaddr_in *)msg->msg_name;
//
if (msg->msg_namelen < sizeof(*usin))
return -EINVAL;
//
if (usin->sin_family != AF_INET) {
if (usin->sin_family != AF_UNSPEC)
return -EAFNOSUPPORT;
}
// ip
daddr = usin->sin_addr.s_addr;
dport = usin->sin_port;
if (dport == 0)
return -EINVAL;
} else {
// IP TCP_ESTABLISHED
if (sk->sk_state != TCP_ESTABLISHED)
return -EDESTADDRREQ;
// ip
daddr = inet->inet_daddr;
dport = inet->inet_dport;
/* Open fast path for connected socket.
Route will not be used, if at least one option is set.
*/
connected = 1;
}
//
ipc.addr = inet->inet_saddr;
//
ipc.oif = sk->sk_bound_dev_if;
err = sock_tx_timestamp(msg, sk, &ipc.shtx);
if (err)
return err;
//
if (msg->msg_controllen) {
// ipc
err = ip_cmsg_send(sock_net(sk), msg, &ipc);
if (err)
return err;
if (ipc.opt)
free = 1;
connected = 0;
}
//
// inet ip
if (!ipc.opt)
ipc.opt = inet->opt;
// saddr
saddr = ipc.addr;
// ipc.addr
ipc.addr = faddr = daddr;
//
// IP
if (ipc.opt && ipc.opt->srr) {
if (!daddr)
return -EINVAL;
faddr = ipc.opt->faddr;
connected = 0;
}
tos = RT_TOS(inet->tos);
// SOCK_LOCALROUTE
// msg_flags MSG_DONTROUTE
// , tos RTO+ONLINK
if (sock_flag(sk, SOCK_LOCALROUTE) ||
(msg->msg_flags & MSG_DONTROUTE) ||
(ipc.opt && ipc.opt->is_strictroute)) {
tos |= RTO_ONLINK;
connected = 0;
}
//
if (ipv4_is_multicast(daddr)) {
if (!ipc.oif)
ipc.oif = inet->mc_index;
if (!saddr)
saddr = inet->mc_addr;
connected = 0;
}
// , rt
if (connected)
rt = (struct rtable *)sk_dst_check(sk, 0);
//
if (rt == NULL) {
struct flowi fl = { .oif = ipc.oif,
.mark = sk->sk_mark,
.nl_u = { .ip4_u =
{ .daddr = faddr,
.saddr = saddr,
.tos = tos } },
.proto = sk->sk_protocol,
.flags = inet_sk_flowi_flags(sk),
.uli_u = { .ports =
{ .sport = inet->inet_sport,
.dport = dport } } };
struct net *net = sock_net(sk);
security_sk_classify_flow(sk, &fl);
//
err = ip_route_output_flow(net, &rt, &fl, sk, 1);
if (err) {
if (err == -ENETUNREACH)
IP_INC_STATS_BH(net, IPSTATS_MIB_OUTNOROUTES);
goto out;
}
err = -EACCES;
// , SO_BROADCAST
if ((rt->rt_flags & RTCF_BROADCAST) &&
!sock_flag(sk, SOCK_BROADCAST))
goto out;
if (connected)
// , rt
sk_dst_set(sk, dst_clone(&rt->u.dst));
}
// MSG_CONFIRM
//
if (msg->msg_flags&MSG_CONFIRM)
goto do_confirm;
back_from_confirm:
saddr = rt->rt_src;
if (!ipc.addr)
daddr = ipc.addr = rt->rt_dst;
lock_sock(sk);
if (unlikely(up->pending)) {
/* The socket is already corked while preparing it. */
/* ... which is an evident application bug. --ANK */
// ,
release_sock(sk);
LIMIT_NETDEBUG(KERN_DEBUG "udp cork app bug 2
");
err = -EINVAL;
goto out;
}
/*
* Now cork the socket to pend data.
*/
// , IP、 IP、
inet->cork.fl.fl4_dst = daddr;
inet->cork.fl.fl_ip_dport = dport;
inet->cork.fl.fl4_src = saddr;
inet->cork.fl.fl_ip_sport = inet->inet_sport;
up->pending = AF_INET;
do_append_data:
up->len += ulen;
getfrag = is_udplite ? udplite_getfrag : ip_generic_getfrag;
//
err = ip_append_data(sk, getfrag, msg->msg_iov, ulen,
sizeof(struct udphdr), &ipc, &rt,
corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);
// skb sk_write_queue
if (err)
udp_flush_pending_frames(sk);
//corkreq MSG_MORE udp_push_pending_frames
//
else if (!corkreq)
err = udp_push_pending_frames(sk);
else if (unlikely(skb_queue_empty(&sk->sk_write_queue)))
up->pending = 0;
// sk
release_sock(sk);
out:
ip_rt_put(rt);
if (free)
kfree(ipc.opt);
if (!err)
return len;
/*
* ENOBUFS = no kernel mem, SOCK_NOSPACE = no sndbuf space. Reporting
* ENOBUFS might not be good (it's not tunable per se), but otherwise
* we don't have a good statistic (IpOutDiscards but it can be too many
* things). We could add another new stat but at least for now that
* seems like overkill.
*/
if (err == -ENOBUFS || test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) {
UDP_INC_STATS_USER(sock_net(sk),
UDP_MIB_SNDBUFERRORS, is_udplite);
}
return err;
do_confirm:
dst_confirm(&rt->u.dst);
if (!(msg->msg_flags&MSG_PROBE) || len)
goto back_from_confirm;
err = 0;
goto out;
}
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
리눅스 입문~컴퓨터 시스템의 하드웨어의 개요와 리눅스의 주요 기능과 그 구조의 개요~별도의 기사에서 각 Linux의 기능인 프로세스 및 메모리 관리 메커니즘에 대한 자세한 내용을 요약합니다. 입력 장치, 네트워크 어댑터를 통해 컴퓨터에서 처리를 수행하도록 요청 프로세스 관리 메모리 관리 장치 조작 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.