Voyage au centre de la pile TCP/IP de Linux

Historique des versions
Version v0.12004-10-12

Table des matières

Introduction
1. Initialisation de la pile TCP/IP
2. Exemple numéro 1, réception d'un paquet icmp echo-request
Fonction ip_rcv
Fonction ip_rcv_finish
Fonction ip_local_deliver
Fonction (static inline) ip_local_deliver_finish
Fonction icmp_echo
Fonction icmp_reply
3. Exemple numéro 2, ouverture d'une connexion TCP
Commande telnet
La librairie GNU C
Appel système sys_socketcall
Fonction sys_connect
4. Le routage
Modification de la table de routage
La commande route
Fonction ip_rt_ioctl
Fonction fib_convert_rtentry
Fonction fib_new_table
Fonction fib_get_table
Fonction fn_hash_insert
Fonction fn_hash_delete
Routage des paquets entrants
Fonction ip_route_input
Fonction ip_route_input_slow
Fonction fib_lookup
Fonction (static inline) fn_hash_lookup
Fonction fib_semantic_match
5. Netfilter / iptables
Les hook netfilter
6. Les différentes structures
struct sk_buff
struct socket
struct proto_ops
Structure net_device
Structure fib_table
Structure rtentry
Structure rtable

Introduction

Bonjour à tous. Suite à mes essais d'uml[1], et quelques cheveux en moins, je suis enfin parvenu à disposer d'un noyau Linux (2.4.27) que je peux utiliser avec gdb. Voici le suivi de ma découverte - par la pratique - de la pile tcp/ip de Linux. Notez que dans cet exemple, il est question uniquement de TCP/IP version 4. Je ne traite pas, du moins au moment ou j'écris ces lignes, de la couche hardware (par exemple le driver réseau).



[1] User Mode Linux

Chapitre 1. Initialisation de la pile TCP/IP

[exit 1. Too many jobs.]

Chapitre 2. Exemple numéro 1, réception d'un paquet icmp echo-request

Dans ce paragraphe, nous verrons les différentes étapes (fonction) lorsque la machine recoit un paquet réseau. Notez que je passe totalement outre les couches bas niveaux (niveau 2). L'exemple choisi est la réception d'un paquet icmp echo-request (un ping). Le noyau se chargera donc de renvoyer un icmp echo-reply, sans que le mode utilisateur n'intervienne.

Fonction ip_rcv

Une fois les diverses opérations relatives à la couche 2 (liaison) effectuées, si le paquet reçu est du type IP en version 4, alors cette fonction est la première appelée.

On lit skb->nh.iph qui correspond à l'en-tête IP, puis on vérifie quelques champs pour la validation RFC. On renvoie une erreur si : iph->ihl (longueur de l'en-tête IP) est inférieur à 5 ou encore si iph->version != 4 ou si la somme de contrôle (checksum [2]) est incorrecte, ou finalement si la longueur est incorrecte. Ensuite on utilise le système de hook de netfilter (NF_HOOK) pour passer à la fonction suivante, à savoir ip_rcv_finish.

Fonction ip_rcv_finish

Si iptables n'est pas utilisé, ou si les règles n'ont pas supprimé le paquet, on appel cette fonction. En premier lieu elle se charge de calculer la route du paquet, via l'appel de la fonction ip_route_input. En effet, si le noyau agit en tant que routeur, il est normal, et c'est ce que l'on attend, que si le paquet n'est pas à destination de la machine, alors elle le renvoie sur la bonne interface. En revanche, si la destination est locale, alors il faut traiter le paquet. Si le paquet est considéré comme local, la fonction ip_route_input définie le pointeur de fonction struct rtable->u.dst.input à ip_local_deliver. Il en existe cependant d'autres, comme ip_forward quand le noyau agit en tant que routeur, ip_error, et ip_mr_input pour le multicasting.

Fonction ip_local_deliver

Dans un premier temps, on recompose les paquets. Puis NF_HOOK vers la fonction ip_local_deliver_finish avec les paramètres :

  • PF_INET

  • NF_IP_LOCAL_IN

  • skb

  • skb->dev (interface d'entrée)

  • NULL (interface de sortie)

Fonction (static inline) ip_local_deliver_finish

Paramètres :

Cette fonction se charge de délivrer à la coupe supérieure (par exemple TCP, UDP ou bien encore ICMP comme dans cet exemple) le paquet, prêt à être traiter, et contenant toutes les informations relatives à celui-ci. Pour cela, on lit le champ protocole du paquet IP (skb->nh.iph->protocol). Avant tout, on vérifie que le paquet ne correspond pas à une socket de type RAW. Sinon, on utilise ce même numéro pour indexer un tableau (en vérifiant cependant qu'il ne dépasse pas MAX_INET_PROTOCOLS - 1, soit 255). Celui-ci contient, entre autres, un membre handler qui est en fait l'adresse de la fonction qui assurera le traitement de la couche supérieure. Si celle ci renvoie une erreur (valeur négative), on tente de retraiter le paquet. Dans notre cas (icmp echo-request) c'est la fonction icmp_echo qui est appelée.

Fonction icmp_echo

Paramètres :

Cette fonction, comme son nom ne l'indique peut être pas, se charge de traiter les paquets du type ICMP echo request. En fait, elle fait un test sur la variable noyau sysctl_icmp_echo_ignore_all. Si la valeur est égale à 0, alors on définie une structure de type icmp_bxm, qu'on remplie avec les éléments suivants :

  • Un en tête icmp

  • Le type ICMP, ici echo reply (définie via une constante préprocesseur)

  • Un pointeur vers la structure struct sk_buff*skb

  • Définition de l'offset à 0

  • Longueur des données (skb->len)

  • Longuer de l'en tête (sizeof (struct icmphdr))

On appel finalement la fonction icmp_reply.

Fonction icmp_reply



[2] Notez que sur architecture x86, c'est une fonction écrite en assembleur pour des raisons de performances

Chapitre 3. Exemple numéro 2, ouverture d'une connexion TCP

Pour cet exemple, je prend le cas d'une connexion TCP (socket de type STREAM) vers un hôte accessible via IPv4.

Commande telnet

La librairie GNU C

Elle effectue simplement un appel système vers la section intitulée « Appel système sys_socketcall ».

Appel système sys_socketcall

Tout échange entre le mode applicatif (userland, comprendre les applications) et le noyau (la pile TCP/IP) se fait au travers de l'appel système générique sys_socketcall. Celui-ci prend en premier paramètre un numéro correspondant à la fonction « réelle », par exemple sys_connect, sys_read, sys_write, etc..

Fonction sys_connect

Pour commencer, on associe une structure *sock en fonction du numéro fd via la fonction sockfd_lookup. Celle-ci se charge de remplir les différentes structures, comme par exemple le membre ops, qui contient un ensemble de pointeurs vers les fonctions à appeler en fonction des divers états possibles (connect, accept, bind, etc..). Ces fonctions sont elles mêmes des wrapper vers des fonctions plus bas niveau. Par exemple, on a, pour une socket toujours de type STREAM, la valeur connect qui pointe vers la fonction inet_stream_connect.

Chapitre 4. Le routage

Modification de la table de routage

Pour bien comprendre la fonction de recherche de route décrite plus tard, il faut commencer par comprendre comment les routes sont définies dans le noyau. Nous verrons dans un premier temps, l'outil mode utilisateur (userland) qui permet de modifier la table de routage, à savoir la commande (8) route. Puis le traitement des IOCTL[3] par la pile TCP/IP.

La commande route

La fonction principale se charge quasi exclusivement de traiter les paramètres puis d'appeler une des fonctions suivantes :

  • route_info

  • route_edit

Fonction route_edit

Cette fonction, est en fait un simple wrapper vers une autre, définie en fonction de la famille de la route (par exemple inet6, ipx, par défaut inet (IPv4)). Pour une route de type inet, la fonction appelée est INET_setroute.

Fonction INET_setroute

En premier lieu, on déclare rt comme étant une structure de type struct rtentry, celle-ci sera utilisée en argument à la fonction ioctl. Elle contient en fait toutes les informations relative à la route, relevons les champs principaux :

  • struct sockaddr rt_dst

  • struct sockaddr rt_gateway

  • struct sockaddr rt_genmask

  • short rt_metric

  • char *rt_dev

En fait, le rôle de la fonction INET_setroute est tout simplement de remplir ces divers membres avec les valeurs passées en argument de la commande, après avoir bien entendu défini l'ensemble des champs à 0 via un appel à la fonction memset. Ensuite on créer une socket de type AF_INET, SOCK_DGRAM, et le protocole 0. On utilise le descripteur de fichier retourné pour appeler la fonction ioctl. Comme noté précedemment, la structure rt est utilisé en argument final, représentant les données qui seront utilisées par la fonction noyau (à savoir ip_rt_ioctl, définie dans le fichier fib_frontend).

Fonction ip_rt_ioctl

Paramètres :

  • unsigned int cmd

  • void *arg

Les valeurs attendues pour cmd sont :

  • SIOCADDRT (ajout d'une route)

  • SIOCDELRT (suppression d'une route)

On attend que arg soit en fait un pointeur vers une structure de type rtentry.

Cette fonction est chargée de traiter les IOCTL relatifs aux modifications de la table de routage (SIOCADDRT et SIOCDELRT). En premier lieu, on regarde si l'appelant à effectivement les droits de rajouter une route (via les mécanismes des capacities, qui ne sont pas (encore ?) traités dans cet ouvrage). La première action notable de cette fonction est l'appel de fib_convert_rtentry. Si aucune erreur n'est renvoyée, on dispose alors de toute les informations prêt a être utilisée par les fonctions de plus bas niveau.

Si cmd est égale à SIOCDELRT, on fait un appel à fib_get_table avec comme argument req.rtm.rtm_table, puis si aucune erreur n'est renvoyée on utilise le pointeur de fonction tb->tb_delete avec les arguments tb, &.rtm, &rta, &, &req.nlh, NULL) (celui ci pointe en fait vers fn_hash_delete.

Si cmd est égale à SIOCADDRT, on déclare une structure de type fib_table qu'on initialise via l'appel de la fonction fib_new_table avec toujours le paramètre req.rtm.rtm_table. Enfin, si aucunne erreur n'est retournée, on appelle le pointeur de fonction tb->tb_insert avec les même paramètre que pour tb->tb_insert (tb_insert pointe vers la fonction fn_hash_insert).

Fonction fib_convert_rtentry

Paramètres :

  • int cmd

  • struct nlmsghdr *nl

  • struct rtmsg *rtm

  • struct kern_rta *rta

  • struct rtentry *r

Cette fonction se charge de préparée une structure de type nlmsghdr, une autre de type rtsmg et d'une troisième de type kern_rta à partir de l'argument de type struct rtentry.

struct nlmsghdr
{
	__u32		nlmsg_len;	/* Length of message including header */
	__u16		nlmsg_type;	/* Message content */
	__u16		nlmsg_flags;	/* Additional flags */
	__u32		nlmsg_seq;	/* Sequence number */
	__u32		nlmsg_pid;	/* Sending process PID */
};
	

struct rtmsg
{
	unsigned char		rtm_family;
	unsigned char		rtm_dst_len;
	unsigned char		rtm_src_len;
	unsigned char		rtm_tos;

	unsigned char		rtm_table;	/* Routing table id */
	unsigned char		rtm_protocol;	/* Routing protocol; see below	*/
	unsigned char		rtm_scope;	/* See below */	
	unsigned char		rtm_type;	/* See below */

	unsigned		rtm_flags;
};
	  

struct kern_rta
{
	void		*rta_dst;       /* destination de la route */
	void		*rta_src;       /* source eventuelle de la route */
	int		*rta_iif;       /* interface d'entrée */
	int		*rta_oif;       /* interface de sortie */
	void		*rta_gw;        /* gateway*/
	u32		*rta_priority;  /* metric */
	void		*rta_prefsrc;   /*  */
	struct rtattr	*rta_mx;
	struct rtattr	*rta_mp;
	unsigned char	*rta_protoinfo;
	u32		*rta_flow;
	struct rta_cacheinfo *rta_ci;
	struct rta_session *rta_sess;
};
	  

L'affectation s'effectue de la manière suivante :

  • Mise à zero des structures rtm et rta

  • Si r->rt_dst.sa_family est différent de AF_INET on renvoie une erreur

  • Déclaration et affectation d'une variable de type int nommée plen à 32 (en fait, c'est le masque par défaut de la route)

  • On attribue à ptr (un pointeur vers u32) l'adresse de destination de la structure rtentry

  • Si r->rt_flags est différent de RTF_HOST (si c'est une route vers un réseau et non une seule machine) on redéfinie plen avec la valeur numérique (0 à 32) du masque de la route

  • On définie nl->nlmsg_flags à NLM_F_REQUEST (définie via instruction préprocesseur à 1)

  • On définie nl->nlmsg_pid et nl->nmlsg_seq à 0

  • On définie nl->nlmsg_len à NLMSG_LENGTH[4](sizeof(*rtm))

  • Si on supprime une route, on redéfinie nlmsg_flags à 0 et nlmsg_type à RTM_DELROUTE

  • Sinon on positionne nlmsg_type à RTM_NEWROUTE, nmlsg_flags à NLM_F_REQUEST | NLM_F_CREATE et rtm->rtm_protocol à RTPROT_BOOT (3)

  • On affecte plen (le masque) à rtm->rtm_dst_len

  • On affecte rta->rta_dst avec la variable ptr

  • On définie rta->rta_prioriry avec une eventuelle valeur de metric

  • Si la valeur de r->rt_flags est égale à RTF_REJECT on définie rtm->rtm_scope à RT_SCOPE_HOST et rtm->rtm_type à RTN_UNREACHABLE (injoignable) puis la fonction retourne 0

  • Si r->dev n'est pas nulle (donc elle contient un nom (char *) d'un périphérique) on affecte le numéro correspondant à rta->rta_oif

  • On fait maintenant pointer ptr vers l'adresse source de la passerelle

  • Si ptr est vrai et que le type de la passerelle est bien AF_INET, on définie rta->rta_gw à ptr

  • Si cmd == SIOCDELRT (suppression d'une route), la fonction s'arrête là retourne 0 (pas d'erreur)

  • Si r->rt_flags correspond à RTF_GATEWAY mais que rta->rta_gw ne correspond à rien, on retourne l'erreur EINVAL

  • Si rtm->rtm_scope égal à RT_SCOPE_NOWHERE, on rédéfinie cette variable à RT_SCOPE_LINK

  • MTU ou WINDOW ou IRTT

  • On retourne 0 (pas d'erreur)

Fonction fib_new_table

Si CONFIG_IP_MULTIPLE_TABLES n'est pas définie, c'est une simple macro vers fib_get_table.

Fonction fib_get_table

Si CONFIG_IP_MULTIPE_TABLES n'est pas activé, renvoie local_table ou main_table.

Fonction fn_hash_insert

Fonction fn_hash_delete

Routage des paquets entrants

Fonction ip_route_input

Cette fonction vérifie dans une table de routage déjà calculée (qui sert donc de cache) s'il n'existe pas une entrée correspondante. Notez que la fonction utilisant un hash (index calculé en fonction de l'adresse de destination, de source et du champ ToS), elle peut entraîner des collisions. C'est pourquoi la valeur associée correspond en fait à une liste chaînée. Finalement, si une entrée valide est détectée, on vérifie que les points suivants correspondent :

  • l'adresse de destination

  • l'adresse source

  • l'interface d'entrée

  • l'interface de sortie (nulle)

  • le champ ToS (Type of Service)

Si c'est le cas, alors on remplit le membre dst, un pointeur vers une structure de type dst_entry. Celle-ci contient, entre autre, un pointeur vers la fonction à utiliser. Par exemple ip_local_deliver pour un paquet devant être traité localement. Si aucune entrée est trouvée, alors on calcule la route à l'aide de la fonction ip_route_input_slow.

Fonction ip_route_input_slow

Définition des paramètres

Si la route n'a pas déjà été calculée, c'est ici que l'opération s'effectue. Dans un premier temps, on vérifie - via un test sur le pointeur (struct net_device) in_dev - que le protocole IP est activé sur l'interface. Ensuite, on effectue les tests suivants sur l'adresse source :

  • adresse source différent de 0xff000000

de même pour l'adresse de destination :

  • masque de 0xf0000000 (BADCLASS), qui correspond à l'intervalle de 240.0.0.0 à 240.255.255.255

  • masque de 0x7f000000 (LOOPBACK), de 127.0.0.0 à 127.255.255.255

  • masque de 0xff000000 (ZERONET), de 255.0.0.0 à 255.255.255.255

[Now, we are ready to route packet.]

Pour calculer la route du paquet, on fait appel à la fonction fib_lookup avec les paramètres suivants :

  • struct flowi &fl

  • struct fib_result res

Cette fonction se charge de calculer la route à partir des quelques éléments passés en argument. La structure flowi est définie de la manière suivante :

	struct flowi fl = { .nl_u = { .ip4_u =
				      { .daddr = daddr,
					.saddr = saddr,
					.tos = tos,
					.scope = RT_SCOPE_UNIVERSE,
#ifdef CONFIG_IP_ROUTE_FWMARK
					.fwmark = skb->nfmark
#endif
				      } },
			    .iif = dev->ifindex };
	  

Une fois l'appel de la fonction fib_lookup effectué, on applique les éventuelles règles de translation d'adresse[5]. Puis, en fonction de la valeur res.type, on effectue divers traitements.

  • RTN_BROADCAST

  • RTN_LOCAL

  • FORWARD (implicite)

Forward

On positionne rth->u.dst.input à ip_forward et rth->u.dst.output à ip_output.

RTN_BROADCAST

On valide la source, on définie res.type à RTN_BROADCAST puis on continue le point suivant comme pour un paquet de destination locale.

RTN_LOCAL

On commence par vérifier que la source est valide, si c'est le cas on fait un jump sur local_input.

On alloue une structure rth (celle utilisée par le cache dans ip_route_input) qu'on remplie à l'aide de divers éléments. On positionne rth->u.dst.input à ip_local_deliver.

Fonction fib_lookup

Définition des paramètres :

  • const struct flowi *flp

  • struct fib_result *res

Si CONFIG_IP_MULTIPLE_TABLES est faux, c'est une fonction (déclarée en static inline) qui se charge de parcourir les deux tables de routage local et main à l'aide de la fonction pointée par le membre tb_lookup (en l'occurence fn_hash_loopkup). Sinon, c'est une fonction qui va parcourir l'ensemble des tables disponibles.

Fonction (static inline) fn_hash_lookup

Paramètres :

On peut dire que nous voici dans la fonction du calcul de le route à proprement parler.

Fonction fib_semantic_match

Paramètres :

  • int type

  • struct fib_info *fi

  • const struct flowi *flp

  • struct fib_result *res



[3] Input/Output ConTrol

[4] ((len)+NLMSG_ALIGN(sizeof(struct nlmsghdr)))

[5] Network Address Translation

Chapitre 5. Netfilter / iptables

Table des matières

Les hook netfilter

Les hook netfilter

L'intégration de netfilter, le firewall de Linux, se fait au travers de hook [6], de la façon suivante. Les différents passages de fonctions s'effectuent via l'appel de NF_HOOK, une constante préprocesseur, avec les paramètres suivants :

  • protocole du HOOK, exemple : PF_INET pour IPv4

  • chaîne, exemple : NF_IP_PRE_ROUTING, NF_IP_LOCAL_IN, etc

  • pointeur vers une structure struct sk_buff, qui contient en fait des données relative au paquet

  • interface d'entrée

  • interface de sortie (peut être NULL)

  • la fonction à appeler si le paquet n'est pas supprimé

Ainsi, si CONFIG_NETFILTER, n'est pas définie, la constante NF_HOOK est définie à :

	  #define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb)
	

se résumant à un simple appel de la fonction okfn, avec le paramètre sbk.



[6] on peut traduire hook par crochet, ou bien encore intercepteur

Chapitre 6. Les différentes structures

struct sk_buff

	  struct sk_buff {
	  /* These two members must be first. */
	  struct sk_buff	* next;			/* Next buffer in list 				*/
	  struct sk_buff	* prev;			/* Previous buffer in list 			*/
	  
	  struct sk_buff_head * list;		/* List we are on				*/
	  struct socket	*sk;			/* Socket we are owned by 			*/
	  struct timeval	stamp;			/* Time we arrived				*/
	  struct net_device	*dev;		/* Device we arrived on/are leaving by		*/
	  struct net_device	*real_dev;	/* For support of point to point protocols 
	  (e.g. 802.3ad) over bonding, we must save the
	  physical device that got the packet before
	  replacing skb->dev with the virtual device.  */
	  
	  /* Transport layer header */
	  union
	  {
	  struct tcphdr	*th;
	  struct udphdr	*uh;
	  struct icmphdr	*icmph;
	  struct igmphdr	*igmph;
	  struct iphdr	*ipiph;
	  struct ipv6hdr	*ipv6h;
	  struct spxhdr	*spxh;
	  unsigned char	*raw;
	  } h;
	  
	  /* Network layer header */
	  union
	  {
	  struct iphdr	*iph;
	  struct ipv6hdr	*ipv6h;
	  struct arphdr	*arph;
	  struct ipxhdr	*ipxh;
	  unsigned char	*raw;
	  } nh;
	  
	  /* Link layer header */
	  union 
	  {	
	  struct ethhdr	*ethernet;
	  unsigned char 	*raw;
	  } mac;
	  
	  struct  dst_entry	*dst;
	  struct	sec_path	*sp;
	  
	  /* 
	  * This is the control buffer. It is free to use for every
	  * layer. Please put your private variables there. If you
	  * want to keep them across layers you have to do a skb_clone()
	  * first. This is owned by whoever has the skb queued ATM.
	  */ 
	  char		cb[48];	 
	  
	  unsigned int 	len;			/* Length of actual data			*/
	  unsigned int 	data_len;
	  unsigned int	csum;			/* Checksum 					*/
	  unsigned char 	local_df,
	  cloned, 		/* head may be cloned (check refcnt to be sure). */
	  pkt_type,		/* Packet class					*/
	  ip_summed;		/* Driver fed us an IP checksum			*/
	  __u32		priority;		/* Packet queueing priority			*/
	  atomic_t	users;			/* User count - see datagram.c,tcp.c 		*/
	  unsigned short	protocol;		/* Packet protocol from driver. 		*/
	  unsigned short	security;		/* Security level of packet			*/
	  unsigned int	truesize;		/* Buffer size 					*/
	  
	  unsigned char	*head;			/* Head of buffer 				*/
	  unsigned char	*data;			/* Data head pointer				*/
	  unsigned char	*tail;			/* Tail pointer					*/
	  unsigned char 	*end;			/* End pointer					*/
	  
	  void 		(*destructor)(struct sk_buff *);	/* Destruct function		*/
	  #ifdef CONFIG_NETFILTER
	  /* Can be used for communication between hooks. */
	  unsigned long	nfmark;
	  /* Cache info */
	  __u32		nfcache;
	  /* Associated connection, if any */
	  struct nf_ct_info *nfct;
	  #ifdef CONFIG_NETFILTER_DEBUG
	  unsigned int nf_debug;
	  #endif
	  #endif /*CONFIG_NETFILTER*/
	  
	  #if defined(CONFIG_HIPPI)
	  union{
	  __u32	ifield;
	  } private;
	  #endif
	  
	  #ifdef CONFIG_NET_SCHED
	  __u32           tc_index;               /* traffic control index */
	  #endif
	  };
	

struct socket

	  struct socket
	  {
	  socket_state		state;

	  unsigned long		flags;
	  struct proto_ops	*ops;
	  struct inode		*inode;
	  struct fasync_struct	*fasync_list;	/* Asynchronous wake up list	*/
	  struct file		*file;		/* File back pointer for gc	*/
	  struct sock		*sk;
	  wait_queue_head_t	wait;

	  short			type;
	  unsigned char		passcred;
	  };
	

struct proto_ops

	  struct proto_ops {
	  int	family;

	  int	(*release)	(struct socket *sock);
	  int	(*bind)		(struct socket *sock, struct sockaddr *umyaddr,
	  int sockaddr_len);
	  int	(*connect)	(struct socket *sock, struct sockaddr *uservaddr,
	  int sockaddr_len, int flags);
	  int	(*socketpair)	(struct socket *sock1, struct socket *sock2);
	  int	(*accept)	(struct socket *sock, struct socket *newsock,
	  int flags);
	  int	(*getname)	(struct socket *sock, struct sockaddr *uaddr,
	  int *usockaddr_len, int peer);
	  unsigned int (*poll)	(struct file *file, struct socket *sock, struct poll_table_struct *wait);
	  int	(*ioctl)	(struct socket *sock, unsigned int cmd,
	  unsigned long arg);
	  int	(*listen)	(struct socket *sock, int len);
	  int	(*shutdown)	(struct socket *sock, int flags);
	  int	(*setsockopt)	(struct socket *sock, int level, int optname,
	  char *optval, int optlen);
	  int	(*getsockopt)	(struct socket *sock, int level, int optname,
	  char *optval, int *optlen);
	  int   (*sendmsg)	(struct socket *sock, struct msghdr *m, int total_len, struct scm_cookie *scm);
	  int   (*recvmsg)	(struct socket *sock, struct msghdr *m, int total_len, int flags, struct scm_cookie *scm);
	  int	(*mmap)		(struct file *file, struct socket *sock, struct vm_area_struct * vma);
	  ssize_t (*sendpage)	(struct socket *sock, struct page *page, int
	  offset, size_t size, int flags);
	  };
	

Structure net_device

Structure fib_table

Structure rtentry

Structure rtable