[mpls-linux-general] [Request For Comments]- Peer Review - Modularize mpls_tunnel
Status: Beta
Brought to you by:
jleu
|
From: Ramon C. <cas...@in...> - 2003-11-20 13:26:09
|
Hi James/all, A first step towards mpls_tunnel modularization: module_init(mpls_tunnel_init_module); module_exit(mpls_tunnel_exit_module); /**** * EXPORTED SYMBOLS **/ EXPORT_SYMBOL(mpls_tunnel_create); EXPORT_SYMBOL(mpls_tunnel_destroy); EXPORT_SYMBOL(mpls_tunnel_get_by_name); EXPORT_SYMBOL(mpls_tunnel_get); EXPORT_SYMBOL(mpls_tunnel_put); __deprecated mpls_tunnel_locate(struct ifreq *ifr,int create) This has not even been compiled :) we are still syncing efforts... Comments welcome. Regards, Ramon PS: James, maybe you should create mpls-linux-devel@... I'm not very comfortable dumping patches and files here :) but we need to expose them! // ------------------------------------------------------------------- // Ramon Casellas - GET/ENST/INFRES/RHD/A508 - cas...@in... // 37/39 rue Dareau 75014 Paris -- http://perso.enst.fr/~casellas 8<--------8<-------------8<--------------8<-------- /***************************************************************************** * MPLS * An implementation of the MPLS (MultiProtocol Label * Switching) Architecture for Linux. * * __THIS_FILE__ * * Management of MPLS tunnels, virtual devices named by default * mpls%d and that can be managed using userspace tools like * ip route, ifconfig, etc. * * Remember, that as per RFC, LSPs are unidirectional. * * Usage: * Creation : mpls_tunnel_create * Destruction : mpls_tunnel_destroy * Add/Rem MOI : ioctl on device * - SIOCMPLSTUNNELADDOUT : Set the MOI to the tunnel * - SIOCMPLSTUNNELDELOUT : remove the MOI from the tunnel * * EXPORT_SYMBOL(mpls_tunnel_create); * EXPORT_SYMBOL(mpls_tunnel_destroy); * EXPORT_SYMBOL(mpls_tunnel_get_by_name); * EXPORT_SYMBOL(mpls_tunnel_get); * EXPORT_SYMBOL(mpls_tunnel_put); * * $Id$ * * Authors: * James Leu <jl...@mi...> * Ramon Casellas <cas...@in...> * * (c) 1999-2003 James Leu <jl...@mi...> * (c) 2003 Ramon Casellas <cas...@in...> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * ChangeLog * v-0.1 20031120 RCAS: [RFC] (Rewrite, Format and Comment :-)) * v-0.2 20031120 RCAS: static & exported symbols *****************************************************************************/ #include <linux/config.h> #include <linux/in.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/skbuff.h> #include <linux/if_arp.h> #include <linux/netdevice.h> #include <net/mpls.h> /** * MODULE Information and attributes * **/ const char* mpls_tunnel_name = "mpls_tunnel"; const char* mpls_tunnel_string = "MultiProtocol Label Switching Tunnel Module"; const char* mpls_tunnel_copyright = "(c) 1999-2003 JLeu -- (c) 2003 RCas"; MODULE_AUTHOR("James Leu <jl...@mi...> -- Ramon Casellas <cas...@in...>"); MODULE_DESCRIPTION("MultiProtocol Label Switching protocol"); MODULE_LICENSE("GPL"); /***** ================ DATA TYPES AND STRUCTURES ===================== struct mpls_tunnel_private { struct mpls_out_info *mtp_moi; // pointer to the MOI. struct net_device *mtp_dev; // pointer to our "mentor device" struct sockaddr mtp_dest; // dest = &ifr->ifr_dstaddr; struct mpls_tunnel_private* next; // this->next in list struct net_device_stats stat; // MIB for this device }; net_device +================+ | name = mpls%d | +================+- - -+ @2L| name = mpls1 | | +=================+- - -+- -.-+ @1L| name = mpls0 | | . | +- - - - - - - - -+- - -+ .- -+ | ... |s_ptr| +- - - - - - - - -+- - -+ |mpls_ptr = NULL | | mpls_tunnel_private +- - - - - - - - -+- - -+ +===========+ | ... |priv*}----@2->|moi | +- - - - - - - - -+- - -+ +===========+ - - + |mpls_tunnel_priv*}----@1->|moi |v=@2L| +-----------------+ / +- - - - - -+ - - + | ... | / |mtp_dev=@1L|. . | +=================+ / +- - - - - -+ - --+ / | . . . |t=@3 | mpls_tunnel_list +- - - - - -+=====+ | next=@2 | +===========+ in fact, we have: kmalloc (sizeof(net_device)+sizeof(mpls_tunnel_private) +=================+ @1L | name = mpls0 | ^ +- - - - - - - - -+ | | ... | | +- - - - - - - - -+ | |mpls_ptr = NULL | | +- - - - - - - - -+ | | ... | | +- - - - - - - - -+ | |mpls_tunnel_priv*|--+ | +- - - - - - - - -+ | | | ... | | | +=================+ | | |moi |<-+ | +- - - - - - - - -+ +--<|mtp_dev=@1L | +- - - - - - - - -+ | . . . | +- - - - - - - - -+ | next=@2 | +=================+ - Any reason why we keep a list of pointers to the priv structs and not a list of the actual netdev addresses? ================ TUNNEL MANAGEMENT VIA ifreq ================================= struct ifreq { union { char ifrn_name[IFNAMSIZ]; ... } ifr_ifrn; union { struct sockaddr ifru_addr; struct sockaddr ifru_dstaddr; ... short ifru_flags; int ifru_mtu; char * ifru_data; ... } ifr_ifru; }; **** ================ TUNNEL MANAGEMENT : Creation ================================ **** * MPLS SUBSYSTEM KERNEL CORE * ==================================================== * ifreq | * |1 | * v Qdisc * +-------------------------+ +-------------------------+ +-------+ * | mpls_tunnel_create |-4-->| register_netdevice(lck) |---7->| queues| * |2 dev~kmalloc/setup |<-9-r+-------------------------+<-8-r-+-------+ * |3 dev->init = | | ^ 5b-ntfy REG * | mpls_tunnel_dev_init | | | +----------+ * +-------------------------+<10,11----->|*dev_hold | * | | | +----------+ * | +-----------------+ | | * | |mpls_tunnel_init |<--5-+ | * | +-----------------+-r--6--+ * |12 * v * +-------------------------+ * | mpls_tunnel_link | * +-------------------------+ ================ TUNNEL MANAGEMENT : Destruction ============================= **** * MPLS SUBSYSTEM KERNEL CORE * ==================================================== * ifreq | * |1 | * v 2L open? Qdisc * +-------------------------+ +---------------------------+ +-------+ * | mpls_tunnel_destroy |-2-->| unregister_netdevice(lck) |---5->|dv/shut| * +-------------------------+ +---------------------------+<-6-r-+-------+ * +-----------------+ | ^ | ^ 13(todo) 5b-ntfy * | mpls_tnl_close |<--3-+ | | | | ^ UNREGIS * +-----------------+-r--4--+ | | | | * | | | |15 * +-----------------+ | | | | * | mpls_tnl_uninit |<----7----+ | | | * + |-r--12------+ | | * +-----------------+ | | * |8 ^ \10,11 | | * v |9 +---v | | * +-----------------+ +---------+ | | * | mpls_tnl_unlink | |*dev_put | | | * +-----------------+ +---------+ | | * +-----------------+ | | * | mpls_tnl_destruc|<--- 14 ----------+ | * +-----------------+--------------------+ * free_netdev ? ******************************************************************/ /**** * Single linked list of MPLS tunnels. ***/ struct mpls_tunnel_private* mpls_tunnel_list; /**** * ... and corresponding readers/writers rwlock_t. **/ rwlock_t mpls_tunnel_lock = RW_LOCK_UNLOCKED; // mpls_dev2priv(MPLSDEV) Obtain private MPLS extension for a netdevice // mpls_priv2dev(MPLSPRIV) Obtain mentor netdevice for a given (private) tunnel #define mpls_dev2priv(MPLSDEV) \ ((struct mpls_tunnel_private *)((MPLSDEV)->priv) #define mpls_priv2dev(MPLSPRIV) \ ((struct net_device *)((MPLSPRIV)->ntp_dev) #define mpls_dev2mtp(MPLSDEV) mpls_dev2priv(MPLSDEV) #define mpls_mtp2dev(MPLSDEV) mpls_priv2dev(MPLSDEV) /**** * mpls_tunnel_link - link(add) the netdevice to the tunnel list * @dev: device object to link. * * Adds the netdevice "dev" to the list of allocated mpls%d tunnels. * Locks the mpls_tunnel_list using mpls_tunnel_lock. "dev" is the * new head list. * * Called by mpls_tunnel_create after having created (alloced) the * virtual device, and after having dev_hold(dev) it. ***/ static void mpls_tunnel_link(struct net_device *dev) { BUG_ON(!dev); BUG_ON(!mpls_dev2mtp(dev)); write_lock_bh(&mpls_tunnel_lock); mpls_dev2mtp(dev)->next = mpls_tunnel_list; mpls_tunnel_list = mpls_dev2mtp(dev); write_unlock_bh(&mpls_tunnel_lock); } /**** * mpls_tunnel_unlink - unlink(remove) the netdevice from the tunnel list * @dev: device object to unlink. * * Removes the netdevice "dev" to the list of allocated mpls%d tunnels. * Locks the mpls_tunnel_list using mpls_tunnel_lock. * * "dev" is now outside the list... Does not affect ownership. The caller * is responsible for dev_putting the device. ***/ static void mpls_tunnel_unlink(struct net_device *dev) { BUG_ON(!dev); BUG_ON(!mpls_dev2mtp(dev)); struct mpls_tunnel_private **ptr; write_lock_bh(&mpls_tunnel_lock); for(ptr = &mpls_tunnel_list;*ptr;ptr = &((*ptr)->next)) if(*ptr == mpls_dev2mtp(dev)) (*ptr) = mpls_dev2mtp(dev)->next; break; write_unlock_bh(&mpls_tunnel_lock); } /***** * mpls_tunnel_create -- allocates a "by default" "mpls%d", registers the net_ * device with the core kernel, and at the end adds it to the list of tunnels * by means of mpls_tunnel_link * @ifr: request ***/ struct net_device* mpls_tunnel_create(struct ifreq *ifr) { struct net_device *dev = NULL; /* Pointer to the created device */ BUG_ON(!ifr); /* * This is a hack. we allocate the concat of a net_device plus a * mpls_tunnel_private struct. Uhmm,... */ dev = (struct net_device*) kmalloc(sizeof(struct net_device) + sizeof(struct mpls_tunnel_private), GFP_KERNEL); if (!dev) goto err_kmalloc; memset(dev,0,sizeof(struct net_device) + sizeof(struct mpls_tunnel_private)); /* * User Constructor callback... The rest of callbacks will be set by it. */ dev->init = mpls_tunnel_dev_init; #if LINUX_VERSION_CODE < 0x020575 dev->features |= NETIF_F_DYNALLOC; #endif /* * Get the address of the mpls_tunnel_private "extension", that is, the * address of the "wannabe/what would be" the 2nd element of a * net_device array */ dev->priv = (struct mpls_tunnel_private*)(&dev[1]); /* * Back reference to the netdevice */ mpls_dev2mtp(dev)->mtp_dev = dev; /* * Copy the destination address found the request to the sockaddr * in the private extension */ memcpy(&(mpls_dev2mtp(dev)->mtp_dest),&ifr->ifr_dstaddr,sizeof(struct sockaddr)); /* * Get the name "format"/"pattern" from fr->ifr_name */ strcpy(dev->name,ifr->ifr_name); // 20031120 RCAS: the lock was missing (right?) rtnl_lock(); if (dev_alloc_name(dev, dev->name) < 0) goto err_noname; /* Register newly created net_device */ if(register_netdevice(dev) < 0) goto err_register; rtnl_unlock(); // 20031120 RCAS: the lock was missing (right?) -- end /* * Finally, update the kernel alloc'ed name... */ strcpy(ifr->ifr_name,dev->name); /* * and hold a ref and put it the tunnel list */ dev_hold(dev); mpls_tunnel_link(dev); return dev; err_register: err_noname: rtnl_unlock(); kfree(dev); dev = NULL; err_kmalloc: return dev; } /**** * mpls_tunnel_destroy - destroy (via ifreq) a tunnel * 20031120 RCAS: destroy/ unregister_netdevice was missing. (right?) * answer to myself: it was in mpls_ioctl... ***/ void mpls_tunnel_destroy(struct ifreq *ifr) { rtnl_lock(); unregister_netdevice(dev); rtnl_unlock(); } /**** * mpls_tunnel_get_by_name - get a reference given a tunnel's name * * @name: interface name of the virtual device (tunnel) * * 20031120 RCAS: added a dev_hold call, otherwise it is confusing and we break * semantincs and "common sense". **/ struct net_device* mpls_tunnel_get_by_name (const char* name) { BUG_ON(!name); struct net_device *dev = NULL; struct mpls_tunnel_private *ptr; read_lock(&mpls_tunnel_lock); for(ptr = mpls_tunnel_list; ptr; ptr = ptr->next) { if (!strncmp( mpls_priv2dev(ptr)->name , name , IFNAMSIZ)) { dev = mpls_priv2dev(ptr); break; } } read_unlock(&mpls_tunnel_lock); return dev; } /**** * mpls_tunnel_get - get a reference to a tunnel. * @ifr: ifreq data **/ struct net_device* mpls_tunnel_get (struct ifreq* ifr) { return (mpls_tunnel_get_by_name(ifr->ifr_name)); } /**** * mpls_tunnel_put - put (release) a reference to a tunnel. * @ifr: ifreq data **/ struct net_device* mpls_tunnel_put (struct ifreq* ifr) { struct net_device *dev = NULL; read_lock(&mpls_tunnel_lock); for(ptr = mpls_tunnel_list; ptr; ptr = ptr->next) { if (!strncmp( mpls_priv2dev(ptr)->name , ifr->ifr_name , IFNAMSIZ)) { dev = mpls-priv2dev(ptr); break; } } read_unlock(&mpls_tunnel_lock); if (dev) dev_put(dev) return; } /**** * mpls_tunnel_add_out - Sets the MOI for this virtual device. * @dev: netdevice "mpls%d" * * Sets the MOI for this netdevice according to the ifreq ifr * (given the labelspace and the label) * Calls netif_start_queue on the device * * Returns 0 if ok. * Remarks: ifr->ifr_data is a pointer to a mpls_label struct ***/ static int mpls_tunnel_add_out (struct net_device* dev,struct ifreq *ifr) { const char *fn_name = "mpls_tunnel_add_out"; struct mpls_out_info *moi = NULL; struct mpls_label *plabel = NULL; int retval = -ENXIO; MPLS_DEBUG(("%s: enter\n",fn_name)); plabel = &ifr->ifr_data; moi = mpls_get_moi(mpls_label2key(plabel->ml_index, (struct mpls_label*)&ifr->ifr_data)); if (!moi) { retval = -ESRCH; MPLS_DEBUG(("%s: error getting node in radix tree\n",fn_name)); return retval; } mpls_dev2mtp(dev)->mtp_moi = moi; dev->mtu = moi->moi_mtu; dev->iflink = ml.ml_index; netif_start_queue(dev); MPLS_DEBUG(("%s: exit\n",fn_name)); return 0; } /**** * mpls_tunnel_del_out - Resets the MOI for this virtual device to NULL * @dev: netdevice "mpls%d" * @ifr: ifreq * * Resets the MOI for this netdevice. ifr is not used. * * Calls netif_stop_queue on the device * Returns 0 if ok. ***/ static int mpls_tunnel_del_out (struct net_device* dev,struct ifreq *ifr) { const char *fn_name = "mpls_tunnel_del_out"; MPLS_DEBUG(("%s: enter\n",fn_name)); BUG_ON(!dev); BUG_ON(!mpls_dev2mtp(dev)); /* previously held in mpls_tunnel_add_out(...) */ mpls_out_info_release(mpls_dev2mtp(dev)->mtp_moi); mpls_dev2mtp(dev)->mtp_moi = NULL; netif_stop_queue(dev); dev->iflink = 0; MPLS_DEBUG(("%s: exit\n",fn_name)); return 0; } /**** * mpls_tunnel_uninit - Callback called from netdev "unregister_netdevice(dev)" * @dev: virtual device * **/ static void mpls_tunnel_uninit (struct net_device *dev) { mpls_tunnel_unlink(dev); dev_put(dev); } /**** * mpls_tunnel_destructor - say tunnel goodbye. * @dev: mpls tunnel * * This callback gets called when the core system destroys the net_device. * Remember that it was allocated with kmalloc(netdev + privdata), and * dev->priv points to the "extension" (privdata). So we just reset to * NULL. Don't kfree(dev->priv); * * cf. dev.c "It must be the very last action, after this 'dev' may point * to freed up memory." ***/ static void mpls_tunnel_destructor(struct net_device *dev) { dev->priv = NULL; } /**** * mpls_tunnel_xmit - transmit a socket buffer via the device. * @skb: data * @dev: mpls tunnel * * This callback gets called when the core system wants to * send a socket buffer. the "mpls_output2" symbol will take * care of it. This only happens of course if someone set a * valid MOI (e.g. PUSH/.../SET) for the device using IOCTL ***/ static int mpls_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) { const char *fn_name = "mpls_tunnel_xmit"; const char *err_nomoi = "MOI was invalid"; struct mpls_push_data mpr = { 0,255,0,1,0 }; MPLS_DEBUG(("%s: enter\n",fn_name)); if(skb->protocol == __constant_htons(ETH_P_MPLS_UC)) { mpr.bos = 0; } else { mpr.bos = 1; } dev->trans_start = jiffies; if (mpls_dev2mtp(dev)->mtp_moi) { mlps_dev2mtp(dev)->stat.tx_packets++; mpls_dev2mtp(dev)->stat.tx_bytes += skb->len; MPLS_DEBUG(("%s: exit\n",fn_name)); return mpls_output2(skb,mpls_dev2mtp(dev)->mtp_moi,&mpr); } dev_kfree_skb(skb); mtp->stat.tx_errors++; MPLS_DEBUG(("%s: exit - %s\n",fn_name, err_nomoi)); return 0; } /**** * mpls_tunnel_get_stats - get sender statistics for this tunnel * @dev: virtual "mpls%d" device. * ***/ static struct net_device_stats* mpls_tunnel_get_stats(struct net_device *dev) { return &((mpls_dev2mtp(dev))->stat); } /**** * mpls_tunnel_ioctl - entry point for ioctls, is a callback of netdevice * @dev: virtual "mpls%d" device. * @ifr: ifreq for the IOCTL request * @cmd: either: * - SIOCMPLSTUNNELADDOUT : Set the MOI to the tunnel * - SIOCMPLSTUNNELDELOUT : remove the MOI from the tunnel * Returns 0 if Ok. -ENOSYS otherwise. ***/ static int mpls_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr,int cmd) { int retval = -ENOSYS; switch (cmd) { case SIOCMPLSTUNNELADDOUT: retval = mpls_tunnel_add_out(dev,ifr); break; case SIOCMPLSTUNNELDELOUT: retval = mpls_tunnel_del_out(dev,ifr); break; default: break; } return retval; } /**** * mpls_tunnel_change_mtu - set a new for the tunnel. * @dev: virtual "mpls%d" device. * @new_mtu: new value * Returns 0 if Ok. -EINVAL otherwise. ***/ static int mpls_tunnel_change_mtu(struct net_device *dev, int new_mtu) { if (new_mtu < 4 || new_mtu > mpls_dev2mtp(dev)->mtp_moi->moi_mtu) return -EINVAL; dev->mtu = new_mtu; return 0; } /*** * mpls_tunnel_init_gen - generic dev->init function * @dev : netdevice object that has been created. * * Init callback for the recently created tunnel. Sets the other callbacks * (transmit -- unidir, we are ~"ingress" --, stats, ioctl, mtu, etc) * and sets the default device (tunnel) attributes. **/ static void mpls_tunnel_init_gen(struct net_device *dev) { /* Callbacks */ dev->uninit = mpls_tunnel_uninit; dev->destructor = mpls_tunnel_destructor; dev->hard_start_xmit = mpls_tunnel_xmit; dev->get_stats = mpls_tunnel_get_stats; dev->do_ioctl = mpls_tunnel_ioctl; dev->change_mtu = mpls_tunnel_change_mtu; // int (*init)(struct net_device *dev); <- set on create // int (*open)(struct net_device *dev); // int (*stop)(struct net_device *dev); /* Properties of mpls%d devices */ dev->type = ARPHRD_MPLS_TUNNEL; dev->hard_header_len = sizeof(u32); dev->mtu = 1500; dev->flags = IFF_NOARP|IFF_POINTOPOINT; dev->iflink = 0; dev->addr_len = 4; } /*** * mpls_tunnel_dev_init(struct net_device *dev) * @dev : netdevice object that has been created. * * The function mpls_tunnel_create sets this function as the init callback * for the created net_device. It will be called by the network core * subsystem. For now, it is just a wrapper. **/ static int mpls_tunnel_dev_init(struct net_device *dev) { mpls_tunnel_init_gen(dev); return 0; } /*** * mpls_tunnel_init() * * Init method called when the module is loaded, initiliazes the * list of created tunnels to zero. **/ static int __init mpls_tunnel_init_module(void) { printk("MPLS %s\n",mpls_tunnel_string); printk("MPLS %s\n",mpls_tunnel_copyright); printk("MPLS version %d.%d%d%d 2003/11/20\n", (MPLS_LINUX_VERSION >> 24) & 0xFF, (MPLS_LINUX_VERSION >> 16) & 0xFF, (MPLS_LINUX_VERSION >> 8) & 0xFF, (MPLS_LINUX_VERSION & 0xFF)); mpls_tunnel_list = NULL; return 0; } /*** * mpls_tunnel_exit() * * Exit method called when the module is unloaded * FIXME : Implement as a work queue ? * * How to do this? we need to destroy all existing tunnels, but we cannot * ReadLock the list of tunnels, since unlink (called from a callback) also * ants to WriteHold the list. **/ static void __exit mpls_tunnel_exit_module(void) { struct net_device *dev = NULL; /* Pointer to the created device */ struct mpls_tunnel_private *ptr; struct ifreq ifr; // This is deadlock zone... for(ptr = mpls_tunnel_list; ptr; ptr = ptr->next) { strcpy(ifr->ifr_name,mpls_mtp2dev(ptr)->name); mpls_tunnel_destroy(&ifr); } printk("MPLS %s exiting\n",mpls_tunnel_string); return; } module_init(mpls_tunnel_init_module); module_exit(mpls_tunnel_exit_module); /**** * EXPORTED SYMBOLS **/ EXPORT_SYMBOL(mpls_tunnel_create); EXPORT_SYMBOL(mpls_tunnel_destroy); EXPORT_SYMBOL(mpls_tunnel_get_by_name); EXPORT_SYMBOL(mpls_tunnel_get); EXPORT_SYMBOL(mpls_tunnel_put); // 8<-----------8<----------8<--------------------------8<-------------------8< // Obsoleted. should be removed. // USE mpls_tunnel_create // USE mpls_tunnel_get // USE mpls_tunnel_get_by_name // USE mpls_tunnel_destroy // 8<-----------8<----------8<--------------------------8<-------------------8< struct net_device* __deprecated mpls_tunnel_locate(struct ifreq *ifr,int create) { BUG(); return NULL; } // 8<-----------8<----------8<--------------------------8<-------------------8< |