linux ping

参考 http://blog.csdn.net/qy532846454/article/details/5429700 学习 自己实现 linux ping 命令,本文主要总结实现过程中的几个重要知识点。

PingPro.c

/*
 * Realize ping command
 *
 * ============================================================================
 * Version: 	0.0
 * File:	PingPro.c
 * Author:	yoyo
 * Time:	2010-03-08
 */

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include "ping.h"

#define MAX_SEND_TIME 5

void ping(char *ip);
void sigint(int sig);
void *signal_set(int signo, void (*func)(int));
void signal_init();

char ip[100];

int main(int argc, char *argv[])
{
	// check parameter
	if(argc != 2)
	{
		printf("Usage: ./pingy IP\n");
		exit(1);
	}

	memcpy(ip, argv[1], sizeof(argv[1]));
	ping(argv[1]);
	
	return 0;
}

void ping(char *ip)
{
	int sockfd;
	struct sockaddr_in dstaddr;
	int times = 0;

	if((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1)
	{
		printf("Create Socket Fail!\n");
		exit(1);
	}

	// fill dstination address
	dstaddr.sin_family = AF_INET;
	dstaddr.sin_port = htons(0);
	if(inet_pton(AF_INET, ip, &dstaddr.sin_addr) == -1)
	{
		printf("Get IP %s Error!\n", ip);
		exit(1);
	}

	// set signal
	signal_init();

	// print header
	print_head(ip);

	// ping
	while(times < MAX_SEND_TIME)
	{
		send_ping(sockfd, &dstaddr);
		recv_ping(sockfd, &dstaddr);
		times++;
	}

	// print statistics
	print_stat(ip);
}

/* ============================================================================ */
/*                                Singal Handle                                 */
/* ============================================================================ */
void sigint(int sig)
{
	print_stat(ip);
	exit(0);
}

void *signal_set(int signo, void (*func)(int))
{
	int ret;
	struct sigaction sig;
	struct sigaction osig;

	sig.sa_handler = func;
	sigemptyset(&sig.sa_mask);
	sig.sa_flags = 0;
#ifdef SA_RESTART
	sig.sa_flags |= SA_RESTART;
#endif /* SA_RESTART */

	ret = sigaction(signo, &sig, &osig);

	if (ret < 0) 
		return (SIG_ERR);
	else
		return (osig.sa_handler);
}

/* Initialization of signal handles. */
void signal_init()
{
	signal_set(SIGINT, sigint);
	signal_set(SIGTERM, sigint);
}

ping.c

#include <stdio.h>
#include <stdlib.h>
#include <netinet/ip_icmp.h>
#include <string.h>
#include "ping.h"

#define ICMP_ECHOREPLY 0
#define ICMP_ECHOREQ 8

// statistics
int nsend = 0, nrecv = 0;
int rrt[10];
int packsize;

void get_data(int rrt[], int size, int *min, int *max, int *avg);
void tv_sub(struct timeval *out,struct timeval *in);

// calculate checksum of icmp header
uint16_t cal_cksum(uint16_t *addr, int len)
{
	int nleft = len;
	uint32_t sum = 0;
	uint16_t *w = addr;
	uint16_t answer = 0;

	while (nleft > 1) {
		sum += *w++;
		nleft -= 2;
	}

	if (nleft == 1) {
		*(unsigned char *)(&answer) = *(unsigned char *)w ;
		sum += answer;
	}

	sum = (sum >> 16) + (sum & 0xffff);
	sum += (sum >> 16);
	answer = ~sum;
	return(answer);
}

void print_head(char *ip)
{
	packsize = sizeof(struct icmp) + sizeof(struct timeval);
	printf("Ping %s with %d bytes of data:\n", ip, packsize);
}

void print_stat(char *ip)
{
	int min_rrt, max_rrt, avg_rrt;
	int lost;
	
	if(nsend < 1)
		return;
	get_data(rrt, nsend, &min_rrt, &max_rrt, &avg_rrt);

	lost = nsend - nrecv;
	printf("========================================================================\n");
	printf("Ping Statistics for %s:\n", ip);
	printf("\tPackets: Send = %d, Received = %d, Lost = %d(%.1f%% lost),\n", nsend, nrecv, lost, (lost / (nsend * 1.0) * 100.0));
	printf("Approximate round trip times in milli-seconds:\n");
	printf("\tMinimum = %dms, Maximum = %dms, Average = %dms\n", min_rrt, max_rrt, avg_rrt);
}

void send_ping(int sockfd, struct sockaddr_in *dstaddr)
{
	char buf[100];
	size_t len = sizeof(struct icmp);
	socklen_t dstlen = sizeof(struct sockaddr_in);
	struct icmp *echo;

	memset(buf, 0, sizeof(buf));
	echo = (struct icmp*)buf;
	echo = (struct icmp *)buf;
	echo->icmp_type = ICMP_ECHOREQ;
	echo->icmp_code = 0;
	echo->icmp_cksum = 0;
	echo->icmp_id = getpid();
	echo->icmp_seq = nsend;
	struct timeval *tval= (struct timeval *)echo->icmp_data;
	gettimeofday(tval,NULL);
	echo->icmp_cksum = cal_cksum((uint16_t *)echo, packsize);

	// send ping message
	if(sendto(sockfd, buf, len, 0, (struct sockaddr*)dstaddr, dstlen) == -1)
		printf("Send Ping Message Error!\n");
	nsend++;
}

void recv_ping(int sockfd, struct sockaddr_in *dstaddr)
{
	char buf[100];
	ssize_t n;
	struct ip *ip;
	struct icmp *icmp;
	socklen_t dstlen = sizeof(struct sockaddr_in);
	int ttl;
	fd_set rset;
	int maxfd = sockfd + 1;
	struct timeval timeo, *tvsend, tvrecv;
	unsigned char *p;
	unsigned char ipaddr[100];
	int time;

	memset(buf, 0, sizeof(buf));
	timeo.tv_sec = 3;
	timeo.tv_usec = 0;

	FD_ZERO(&rset);
	FD_SET(sockfd, &rset);
	gettimeofday(&tvrecv, NULL);

	while(1)
	{
		// set timeout 3s
		if((n = select(maxfd, &rset, NULL, NULL, &timeo)) == -1)
		{
			printf("Select Error!\n");
			exit(1);
		}

		if(n <= 0)
		{
			printf("Request Time Out!\n");
			fflush(stdout);
			break;
		}

		if((n = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)dstaddr, &dstlen)) == -1)
			printf("Recv Ping Message Error!\n");

		// check if icmp
		ip = (struct ip*)buf;
		ttl = ip->ip_ttl;

		// get src ip
		p = (unsigned char *)&ip->ip_src;
		sprintf(ipaddr, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);

		// check if loop
		if(strcmp(ipaddr, "127.0.0.1") == 0)
		{
			//perror("Loop!");
			continue;
		}

		// check if icmp packet
		if(ip->ip_p != IPPROTO_ICMP)
		{
			//perror("Not ICMP Protocol!");
			continue;
		}

		// check if icmp reply
		icmp = (struct icmp*)(buf + sizeof(struct ip));
		if(icmp->icmp_type == ICMP_ECHOREPLY)
		{
			// check id
			if(icmp->icmp_id == getpid())
			{
				tvsend = (struct timeval *)icmp->icmp_data;
				tv_sub(&tvrecv, tvsend);
				time = tvrecv.tv_sec * 1000 + tvrecv.tv_usec / 1000;
				nrecv++;

				printf("\tReply from %s: bytes = %d time = %dms TTL = %d\n", ipaddr, n, time, ttl);
				rrt[nsend - 1] = time;
				break;
			}
			else
			{
				//perror("Not Expected Identifier!");
				continue;
			}
		}
		else
		{
			//perror("Not ICMP Reply Message!");
			continue;
		}
	}
}

/* ================================================================ */
/*                          Auxiliary Function                      */
/* ================================================================ */
/*
 * Function:	get_data
 * Purpose:		get statistics from data rrt array, such as minimum rrt,
 *				maximum rrt, average rrt
 * Parameters:	rrt - round trip time
 *				size - array size
 *				min - store minimum time
 *				max - store maximum time
 *				avg - store average time
 * Return:		none
 */
void get_data(int rrt[], int size, int *min, int *max, int *avg)
{
	int sum = 0;
	int i;

	*min = rrt[0], *max = rrt[0];

	for(i = 0; i < size; i++)
	{
		sum += rrt[i];
		if(rrt[i] < *min)
			*min = rrt[i];
		if(rrt[i] > *max)
			*max = rrt[i];
	}
	*avg = sum / size;
}

void tv_sub(struct timeval *out,struct timeval *in)
{       
	if((out->tv_usec -= in->tv_usec) < 0)
	{       
		--out->tv_sec;
		out->tv_usec += 1000000;
	}
	out->tv_sec -= in->tv_sec;
}

ping 命令的实现主要就是 1) 使用 socket 创建 raw type socket, 使用 ICMP 协议,2) 填充 ICMP 协议数据包, 3) 发送和接收 ICMP 数据包。

    if((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1)

用户空间调用socket 传入如上参数,就是指定 socket 使用 AF_INET/PF_INET,也就是IP protocol family, 指定了地址族/协议族。

/* Supported address families. */
#define AF_UNSPEC	0
#define AF_UNIX		1	/* Unix domain sockets 		*/
#define AF_LOCAL	1	/* POSIX name for AF_UNIX	*/
#define AF_INET		2	/* Internet IP Protocol 	*/
#define AF_AX25		3	/* Amateur Radio AX.25 		*/
#define AF_IPX		4	/* Novell IPX 			*/
#define AF_APPLETALK	5	/* AppleTalk DDP 		*/
#define AF_NETROM	6	/* Amateur Radio NET/ROM 	*/
#define AF_BRIDGE	7	/* Multiprotocol bridge 	*/
#define AF_ATMPVC	8	/* ATM PVCs			*/
#define AF_X25		9	/* Reserved for X.25 project 	*/
#define AF_INET6	10	/* IP version 6			*/
#define AF_ROSE		11	/* Amateur Radio X.25 PLP	*/
#define AF_DECnet	12	/* Reserved for DECnet project	*/
#define AF_NETBEUI	13	/* Reserved for 802.2LLC project*/
#define AF_SECURITY	14	/* Security callback pseudo AF */
#define AF_KEY		15      /* PF_KEY key management API */
#define AF_NETLINK	16
#define AF_ROUTE	AF_NETLINK /* Alias to emulate 4.4BSD */
#define AF_PACKET	17	/* Packet family		*/
#define AF_ASH		18	/* Ash				*/
#define AF_ECONET	19	/* Acorn Econet			*/
#define AF_ATMSVC	20	/* ATM SVCs			*/
#define AF_RDS		21	/* RDS sockets 			*/
#define AF_SNA		22	/* Linux SNA Project (nutters!) */
#define AF_IRDA		23	/* IRDA sockets			*/
#define AF_PPPOX	24	/* PPPoX sockets		*/
#define AF_WANPIPE	25	/* Wanpipe API Sockets */
#define AF_LLC		26	/* Linux LLC			*/
#define AF_CAN		29	/* Controller Area Network      */
#define AF_TIPC		30	/* TIPC sockets			*/
#define AF_BLUETOOTH	31	/* Bluetooth sockets 		*/
#define AF_IUCV		32	/* IUCV sockets			*/
#define AF_RXRPC	33	/* RxRPC sockets 		*/
#define AF_ISDN		34	/* mISDN sockets 		*/
#define AF_PHONET	35	/* Phonet sockets		*/
#define AF_IEEE802154	36	/* IEEE802154 sockets		*/
#define AF_MAX		37	/* For now.. */

/* Protocol families, same as address families. */
#define PF_UNSPEC	AF_UNSPEC
#define PF_UNIX		AF_UNIX
#define PF_LOCAL	AF_LOCAL
#define PF_INET		AF_INET
#define PF_AX25		AF_AX25
#define PF_IPX		AF_IPX
#define PF_APPLETALK	AF_APPLETALK
#define	PF_NETROM	AF_NETROM
#define PF_BRIDGE	AF_BRIDGE
#define PF_ATMPVC	AF_ATMPVC
#define PF_X25		AF_X25
#define PF_INET6	AF_INET6
#define PF_ROSE		AF_ROSE
#define PF_DECnet	AF_DECnet
#define PF_NETBEUI	AF_NETBEUI
#define PF_SECURITY	AF_SECURITY
#define PF_KEY		AF_KEY
#define PF_NETLINK	AF_NETLINK
#define PF_ROUTE	AF_ROUTE
#define PF_PACKET	AF_PACKET
#define PF_ASH		AF_ASH
#define PF_ECONET	AF_ECONET
#define PF_ATMSVC	AF_ATMSVC
#define PF_RDS		AF_RDS
#define PF_SNA		AF_SNA
#define PF_IRDA		AF_IRDA
#define PF_PPPOX	AF_PPPOX
#define PF_WANPIPE	AF_WANPIPE
#define PF_LLC		AF_LLC
#define PF_CAN		AF_CAN
#define PF_TIPC		AF_TIPC
#define PF_BLUETOOTH	AF_BLUETOOTH
#define PF_IUCV		AF_IUCV
#define PF_RXRPC	AF_RXRPC
#define PF_ISDN		AF_ISDN
#define PF_PHONET	AF_PHONET
#define PF_IEEE802154	AF_IEEE802154
#define PF_MAX		AF_MAX

IPPROTO_ICMP 指定了 socket 使用IP 协议族中 具体的哪一个协议,这里是 ICMP。

/* Standard well-defined IP protocols.  */
enum {
  IPPROTO_IP = 0,		/* Dummy protocol for TCP		*/
  IPPROTO_ICMP = 1,		/* Internet Control Message Protocol	*/
  IPPROTO_IGMP = 2,		/* Internet Group Management Protocol	*/
  IPPROTO_IPIP = 4,		/* IPIP tunnels (older KA9Q tunnels use 94) */
  IPPROTO_TCP = 6,		/* Transmission Control Protocol	*/
  IPPROTO_EGP = 8,		/* Exterior Gateway Protocol		*/
  IPPROTO_PUP = 12,		/* PUP protocol				*/
  IPPROTO_UDP = 17,		/* User Datagram Protocol		*/
  IPPROTO_IDP = 22,		/* XNS IDP protocol			*/
  IPPROTO_DCCP = 33,		/* Datagram Congestion Control Protocol */
  IPPROTO_RSVP = 46,		/* RSVP protocol			*/
  IPPROTO_GRE = 47,		/* Cisco GRE tunnels (rfc 1701,1702)	*/

  IPPROTO_IPV6	 = 41,		/* IPv6-in-IPv4 tunnelling		*/

  IPPROTO_ESP = 50,            /* Encapsulation Security Payload protocol */
  IPPROTO_AH = 51,             /* Authentication Header protocol       */
  IPPROTO_BEETPH = 94,	       /* IP option pseudo header for BEET */
  IPPROTO_PIM    = 103,		/* Protocol Independent Multicast	*/

  IPPROTO_COMP   = 108,                /* Compression Header protocol */
  IPPROTO_SCTP   = 132,		/* Stream Control Transport Protocol	*/
  IPPROTO_UDPLITE = 136,	/* UDP-Lite (RFC 3828)			*/

  IPPROTO_RAW	 = 255,		/* Raw IP packets			*/
  IPPROTO_MAX
};

SOCK_RAW 指定创建何种类型的socket。

/**
 * enum sock_type - Socket types
 * @SOCK_STREAM: stream (connection) socket
 * @SOCK_DGRAM: datagram (conn.less) socket
 * @SOCK_RAW: raw socket
 * @SOCK_RDM: reliably-delivered message
 * @SOCK_SEQPACKET: sequential packet socket
 * @SOCK_DCCP: Datagram Congestion Control Protocol socket
 * @SOCK_PACKET: linux specific way of getting packets at the dev level.
 *		  For writing rarp and other similar things on the user level.
 *
 * When adding some new socket type please
 * grep ARCH_HAS_SOCKET_TYPE include/asm-* /socket.h, at least MIPS
 * overrides this enum for binary compat reasons.
 */
enum sock_type {
	SOCK_STREAM	= 1,
	SOCK_DGRAM	= 2,
	SOCK_RAW	= 3,
	SOCK_RDM	= 4,
	SOCK_SEQPACKET	= 5,
	SOCK_DCCP	= 6,
	SOCK_PACKET	= 10,
};

因为我们要自己构建ICMP 网络层数据包,所以使用的是SOCK_RAW, raw socket.

通过上面这张图可以清晰看到各层协议所处的位置。

在 TCP/IP Guide 书中可以看到ICMP message class, type, code, 以及 ICMP Common Message Format




下面看下 用户空间 执行 socket 系统调用,linux 内核都做了些什么,

<umts_sholes_kernel-2.6.32.60>/net/socket.c

SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
{

	retval = sock_create(family, type, protocol, &sock);

int sock_create(int family, int type, int protocol, struct socket **res)
{
	return __sock_create(current->nsproxy->net_ns, family, type, protocol, res, 0);
}

static int __sock_create(struct net *net, int family, int type, int protocol,
			 struct socket **res, int kern)
{
	const struct net_proto_family *pf;

// selinux 走这
	err = security_socket_create(family, type, protocol, kern);

//非 selinux 走这,获取具体地址族/协议族的 socket create 方法,这里需要看下各个地址族/协议族 各自注册socket 接口的地方
	pf = rcu_dereference(net_families[family]);

// 执行 特定于某个协议族的socket create 方法
	err = pf->create(net, sock, protocol);

下面看下协议族如何注册socket 接口

/**
 *	sock_register - add a socket protocol handler
 *	@ops: description of protocol
 *
 *	This function is called by a protocol handler that wants to
 *	advertise its address family, and have it linked into the
 *	socket interface. The value ops->family coresponds to the
 *	socket system call protocol family.
 */
int sock_register(const struct net_proto_family *ops)
{
// 看来是通过这个接口注册 socket
		net_families[ops->family] = ops;

---- sock_register Matches (32 in 30 files) ----
Af_ax25.c (net\ax25):	sock_register(&ax25_family_ops);
Af_bluetooth.c (net\bluetooth):	err = sock_register(&bt_sock_family_ops);
Af_can.c (net\can):	sock_register(&can_family_ops);
Af_decnet.c (net\decnet):	sock_register(&dn_family_ops);
Af_econet.c (net\econet):	sock_register(&econet_family_ops);
Af_ieee802154.c (net\ieee802154):	rc = sock_register(&ieee802154_family_ops);
Af_inet.c (net\ipv4):	(void)sock_register(&inet_family_ops);
Af_inet6.c (net\ipv6):	err = sock_register(&inet6_family_ops);
Af_ipx.c (net\ipx):	sock_register(&ipx_family_ops);
Af_irda.c (net\irda):		rc = sock_register(&irda_family_ops);
Af_iucv.c (net\iucv):	err = sock_register(&iucv_sock_family_ops);
Af_key.c (net\key):	err = sock_register(&pfkey_family_ops);
Af_llc.c (net\llc):	rc = sock_register(&llc_ui_family_ops);
Af_netlink.c (net\netlink):	sock_register(&netlink_family_ops);
Af_netrom.c (net\netrom):	if (sock_register(&nr_family_ops)) {
Af_packet.c (net\packet):	sock_register(&packet_family_ops);
Af_phonet.c (net\phonet):	err = sock_register(&phonet_proto_family);
Af_rds.c (net\rds):	ret = sock_register(&rds_family_ops);
Af_rose.c (net\rose):	sock_register(&rose_family_ops);
Af_rxrpc.c (net\rxrpc):	ret = sock_register(&rxrpc_family_ops);
Af_unix.c (net\unix):	sock_register(&unix_family_ops);
Af_x25.c (net\x25):	sock_register(&x25_family_ops);
Ddp.c (net\appletalk):	(void)sock_register(&atalk_family_ops);
Net.h (include\linux):extern int	     sock_register(const struct net_proto_family *fam);
Pppox.c (drivers\net):	return sock_register(&pppox_proto_family);
Pvc.c (net\atm):	return sock_register(&pvc_family_ops);
Socket.c (drivers\isdn\misdn):	err = sock_register(&mISDN_sock_family_ops);
Socket.c (net): *	sock_register - add a socket protocol handler
Socket.c (net):int sock_register(const struct net_proto_family *ops)
Socket.c (net):EXPORT_SYMBOL(sock_register);
Socket.c (net\tipc):	res = sock_register(&tipc_family_ops);
Svc.c (net\atm):	return sock_register(&svc_family_ops);

af_inet.c

static int __init inet_init(void)
{
	/*
	 *	Tell SOCKET that we are alive...
	 */

	(void)sock_register(&inet_family_ops);

static struct net_proto_family inet_family_ops = {
	.family = PF_INET,
	.create = inet_create,
	.owner	= THIS_MODULE,
};

这样,IPv4 协议的 socket create, 其实是执行inet_create


打包好 ICMP message 之后, 通过 sendto 系统调用,发送 ICMP message

    if(sendto(sockfd, buf, len, 0, (struct sockaddr*)dstaddr, dstlen) == -1)

/*
 *	Send a datagram to a given address. We move the address into kernel
 *	space and check the user space data area is readable before invoking
 *	the protocol.
 */

SYSCALL_DEFINE6(sendto, int, fd, void __user *, buff, size_t, len,
		unsigned, flags, struct sockaddr __user *, addr,
		int, addr_len)
{
	err = sock_sendmsg(sock, &msg, len);

int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
{
	ret = __sock_sendmsg(&iocb, sock, msg, size);

static inline int __sock_sendmsg(struct kiocb *iocb, struct socket *sock,
				 struct msghdr *msg, size_t size)
{
	err = sock->ops->sendmsg(iocb, sock, msg, size);

static const struct proto_ops inet_sockraw_ops = {
	.family		   = PF_INET,
	.owner		   = THIS_MODULE,
	.release	   = inet_release,
	.bind		   = inet_bind,
	.connect	   = inet_dgram_connect,
	.socketpair	   = sock_no_socketpair,
	.accept		   = sock_no_accept,
	.getname	   = inet_getname,
	.poll		   = datagram_poll,
	.ioctl		   = inet_ioctl,
	.listen		   = sock_no_listen,
	.shutdown	   = inet_shutdown,
	.setsockopt	   = sock_common_setsockopt,
	.getsockopt	   = sock_common_getsockopt,
	.sendmsg	   = inet_sendmsg,
	.recvmsg	   = sock_common_recvmsg,
	.mmap		   = sock_no_mmap,
	.sendpage	   = inet_sendpage,
#ifdef CONFIG_COMPAT
	.compat_setsockopt = compat_sock_common_setsockopt,
	.compat_getsockopt = compat_sock_common_getsockopt,
#endif
};

/* Upon startup we insert all the elements in inetsw_array[] into
 * the linked list inetsw.
 */
static struct inet_protosw inetsw_array[] =
{
	{
		.type =       SOCK_STREAM,
		.protocol =   IPPROTO_TCP,
		.prot =       &tcp_prot,
		.ops =        &inet_stream_ops,
		.capability = -1,
		.no_check =   0,
		.flags =      INET_PROTOSW_PERMANENT |
			      INET_PROTOSW_ICSK,
	},

	{
		.type =       SOCK_DGRAM,
		.protocol =   IPPROTO_UDP,
		.prot =       &udp_prot,
		.ops =        &inet_dgram_ops,
		.capability = -1,
		.no_check =   UDP_CSUM_DEFAULT,
		.flags =      INET_PROTOSW_PERMANENT,
       },


       {
	       .type =       SOCK_RAW,
	       .protocol =   IPPROTO_IP,	/* wild card */
	       .prot =       &raw_prot,
	       .ops =        &inet_sockraw_ops,
	       .capability = CAP_NET_RAW,
	       .no_check =   UDP_CSUM_DEFAULT,
	       .flags =      INET_PROTOSW_REUSE,
       }
};

static int __init inet_init(void)
{
	for (q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q)
		inet_register_protosw(q);

int inet_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
		 size_t size)
{
	struct sock *sk = sock->sk;

	/* We may need to bind the socket. */
	if (!inet_sk(sk)->num && inet_autobind(sk))
		return -EAGAIN;

	return sk->sk_prot->sendmsg(iocb, sk, msg, size);
}

struct proto raw_prot = {
	.name		   = "RAW",
	.owner		   = THIS_MODULE,
	.close		   = raw_close,
	.destroy	   = raw_destroy,
	.connect	   = ip4_datagram_connect,
	.disconnect	   = udp_disconnect,
	.ioctl		   = raw_ioctl,
	.init		   = raw_init,
	.setsockopt	   = raw_setsockopt,
	.getsockopt	   = raw_getsockopt,
	.sendmsg	   = raw_sendmsg,
	.recvmsg	   = raw_recvmsg,
	.bind		   = raw_bind,
	.backlog_rcv	   = raw_rcv_skb,
	.hash		   = raw_hash_sk,
	.unhash		   = raw_unhash_sk,
	.obj_size	   = sizeof(struct raw_sock),
	.h.raw_hash	   = &raw_v4_hashinfo,
#ifdef CONFIG_COMPAT
	.compat_setsockopt = compat_raw_setsockopt,
	.compat_getsockopt = compat_raw_getsockopt,
#endif
};

static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
		       size_t len)
{




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值