UDP 전송 패 킷 프로 세 스

UDP 패 킷 을 보 내 는 함 수 는 udp 입 니 다.sendmsg, 사용자 주소 공간 에서 패 킷 을 받 은 다음 커 널 공간 에 값 을 부여 합 니 다.udp_sendmsg 함수 의 주 입력 매개 변 수 는 네 가지 가 있 습 니 다.
(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; }

좋은 웹페이지 즐겨찾기