/*************************************************************************** * File Name : smux.c * Description : smux mean server mux. ***************************************************************************/ #include <asm/uaccess.h> #include <linux/capability.h> #include <linux/module.h> #include <linux/mm.h> #include <linux/in.h> #include <linux/init.h> #include <linux/rtnetlink.h> #include <linux/notifier.h> #ifdef CONFIG_IP_MROUTE #include <linux/inetdevice.h> #endif #include <linux/if_vlan.h> #include <linux/etherdevice.h> #include <linux/ethtool.h> #include <linux/notifier.h> #include <net/rtl/rtl_nic.h> #include <net/rtl/rtl867x_hwnat_api.h> #include <linux/if_smux.h> /* must be after if_vlan.h so no redef error */ #include "../net/bridge/br_private.h" static char vlan_passthru = 1; #ifdef CONFIG_RTL8685_PTM_MII #include "8685_ptm/ptm_mii_sw_wanif.h" #ifdef USE_PTM_MII_WANIF static int is_ptmdev(struct net_device *vdev) { int ret=0; struct smux_dev_info *dev_info; if(vdev==NULL) return ret; dev_info = SMUX_DEV_INFO(vdev); if(dev_info && dev_info->smux_grp && dev_info->smux_grp->real_dev) { char *p=dev_info->smux_grp->real_dev->name; //printk( "%s: vdev=%s, real_dev=%s\n", __FUNCTION__, vdev->name, p ); if(strcmp(p,ALIASNAME_PTM0)==0) ret=1; } return ret; } #endif /*USE_PTM_MII_WANIF*/ #endif /*CONFIG_RTL8685_PTM_MII*/ #ifdef CONFIG_PORT_MIRROR extern void nic_tx_mirror (struct sk_buff *skb); static inline void smux_mirror_pkt(struct sk_buff *skb, const struct smux_dev_info *dev_info, const int flag); #define IN 0x1 #define OUT 0x2 #endif #ifdef DEBUG #define DPRINTK(format, args...) printk(KERN_DEBUG "SMUX: " format, ##args) #else #define DPRINTK(format, args...) #endif //extern unsigned int pvid_per_port[RTL8651_PORT_NUMBER+3]; //#define UNIQUE_MAC_PER_DEV #undef UNIQUE_MAC_PER_DEV /*************************************************************************** Global variables ***************************************************************************/ #define SET_MODULE_OWNER(dev) do { } while (0) static struct list_head SMUX_IGNORE_HEAD; static struct smux_ignore_entry { int proto; struct list_head smuxIgnoreList; }; typedef struct smux_ignore_entry SMUX_IGN_ENTRY; static DEFINE_RWLOCK(smux_lock); static LIST_HEAD(smux_grp_devs); static int smux_device_event(struct notifier_block *, unsigned long, void *); static struct notifier_block smux_notifier_block = { .notifier_call = smux_device_event, }; /* * return value: * 0: NOT match any rule * 1: match smux ignore rule */ int smux_ignore_check(struct sk_buff *skb){ SMUX_IGN_ENTRY *pEntry,*next; list_for_each_entry_safe(pEntry, next, &SMUX_IGNORE_HEAD, smuxIgnoreList){ if(eth_hdr(skb)->h_proto == pEntry->proto){ // printk("[%s:%d] found Commserver Broadcast packet\n", __FUNCTION__, __LINE__); return 1; } } return 0; } #ifdef UNIQUE_MAC_PER_DEV unsigned char wan_dev_def_vid[9]={[0 ... 8]=0};//index 0 is reserved /* * return value: -1 : FAIL */ int allocSmuxDevVid(void) { int i; for (i=1; i<9; i++) { if (!wan_dev_def_vid[i]) break; } if (i<9) return i; return -1; } int freeSmuxDevVid(int vid) { if ((vid >= 9) || (vid <= 0)) return -1; wan_dev_def_vid[vid] = 0; return 0; } #endif /*************************************************************************** Function Definisions ***************************************************************************/ static inline struct smux_group *list_entry_smuxgrp(const struct list_head *le) { return list_entry(le, struct smux_group, smux_grp_devs); } /*************************************************************************** * Function Name: __find_smux_group * Description : returns the smux group of interfaces/devices from list * Returns : struct smux_group. ***************************************************************************/ struct smux_group *__find_smux_group(const char *ifname) { struct list_head *lh; struct smux_group *smux_grp; struct smux_group *ret_smux = NULL; read_lock(&smux_lock); list_for_each(lh, &smux_grp_devs) { smux_grp = (struct smux_group *)list_entry_smuxgrp(lh); if (!strncmp(smux_grp->real_dev->name, ifname, IFNAMSIZ)) { ret_smux = smux_grp; break; } } read_unlock(&smux_lock); return ret_smux; } /* __find_smux_group */ inline struct smux_dev_info *list_entry_smuxdev(const struct list_head *le) { return list_entry(le, struct smux_dev_info, list); } void open_smux_dev_bydev(const struct net_device *dev) { struct list_head *lg, *lh; struct smux_group *smux_grp; struct smux_dev_info * sdev = NULL; read_lock(&smux_lock); list_for_each(lh, &smux_grp_devs){ smux_grp = (struct smux_group *)list_entry_smuxgrp(lh); if (smux_grp->real_dev == dev) { list_for_each(lg, &smux_grp->virtual_devs){ sdev = list_entry_smuxdev(lg); netif_wake_queue(sdev->vdev); } break; } } read_unlock(&smux_lock); } /* open_smux_device */ void open_smux_device(char *ifname) { struct list_head *lg, *lh; struct smux_group *smux_grp; struct smux_dev_info * sdev = NULL; read_lock(&smux_lock); list_for_each(lh, &smux_grp_devs){ smux_grp = (struct smux_group *)list_entry_smuxgrp(lh); if (!strncmp(smux_grp->real_dev->name, ifname, IFNAMSIZ)){ list_for_each(lg, &smux_grp->virtual_devs){ sdev = list_entry_smuxdev(lg); netif_wake_queue(sdev->vdev); } break; } } read_unlock(&smux_lock); } /* open_smux_device */ void close_smux_device(char *ifname) { struct list_head *lg, *lh; struct smux_group *smux_grp; struct smux_dev_info * sdev = NULL; read_lock(&smux_lock); list_for_each(lh, &smux_grp_devs){ smux_grp = (struct smux_group *)list_entry_smuxgrp(lh); if (!strncmp(smux_grp->real_dev->name, ifname, IFNAMSIZ)){ list_for_each(lg, &smux_grp->virtual_devs){ sdev = list_entry_smuxdev(lg); netif_stop_queue(sdev->vdev); } break; } } read_unlock(&smux_lock); } /* close_smux_device */ /*************************************************************************** * Function Name: __find_smux_in_smux_group * Description : returns the smux device from smux group of devices * Returns : struct net_device ***************************************************************************/ static struct net_device *__find_smux_in_smux_group( struct smux_group *smux_grp, const char *ifname) { struct list_head *lh; struct smux_dev_info * sdev = NULL; struct net_device * ret_dev = NULL; read_lock(&smux_lock); list_for_each(lh, &smux_grp->virtual_devs) { sdev = list_entry_smuxdev(lh); if(!strncmp(sdev->vdev->name, ifname, IFNAMSIZ)) { ret_dev = sdev->vdev; break; } } read_unlock(&smux_lock); return ret_dev; } /* __find_smux_in_smux_group */ static struct sk_buff *smux_eat_vlan(struct sk_buff *skb, struct smux_dev_info *info) { struct vlan_hdr *vhdr=NULL; struct net_bridge_port *p; if ((skb->dev->priv_flags & IFF_VSMUX) && (skb->protocol == __constant_htons(ETH_P_8021Q))) { skb = skb_vlan_untag(skb); if (skb) { skb->vlan_tci = 0; } } return skb; } #if defined(CONFIG_RTL_MULTI_ETH_WAN_MAC_BASED) /* * Return Value: * 0: smux device not accept this packet * 1: smux device accept this packet */ static inline int smux_check_mac(struct smux_dev_info *dev_info, struct sk_buff *skb) { struct net_device *vdev; vdev = dev_info->vdev; if(!memcmp(eth_hdr(skb)->h_dest,vdev->dev_addr,ETH_ALEN)) return 1; return 0; } #else /* * Return Value: * 0: smux device not accept this packet * 1: smux device accept this packet */ static inline int smux_check_vlan(struct smux_dev_info *dev_info, struct sk_buff *skb) { struct net_device *vdev; unsigned short tpid_check = skb->protocol; unsigned short vid = 0; struct vlan_hdr *vhdr=NULL; int is_vlan_pkt = 0; if(tpid_check == __constant_htons(ETH_P_8021Q)){ vhdr = (struct vlan_hdr *)(skb->data); vid = (vhdr->h_vlan_TCI)&VLAN_VID_MASK; is_vlan_pkt = 1; } vdev = dev_info->vdev; if (vdev->priv_flags&IFF_VSMUX) { // tagged wan if (!is_vlan_pkt) // untagged packet return 0; if (vid!=dev_info->vid) return 0; } else { // untagged wan if (is_vlan_pkt){ // tagged packet if (vlan_passthru){ // vlan passthrough on untagged bridged wan if (dev_info->proto!=SMUX_PROTO_BRIDGE && !dev_info->brpppoe) return 0; } else { return 0; } } } return 1; } #endif #if defined(CONFIG_RTK_8023AH) || defined(CONFIG_RTK_Y1731) static inline int smux_recv_eoam(struct sk_buff *skb) { struct vlan_hdr *vhdr; if (skb->protocol == cpu_to_be16(ETH_P_8021Q)) { vhdr = (struct vlan_hdr *)skb->data; while (vhdr->h_vlan_encapsulated_proto == cpu_to_be16(ETH_P_8021Q)) { vhdr = vhdr+1; } if (vhdr->h_vlan_encapsulated_proto == htons(0x8809) || vhdr->h_vlan_encapsulated_proto == htons(0x8902)) { // ignore vlan, set skb->protocol so __netif_receive_skb_core() will not do skb_vlan_untag(). vlan_set_encap_proto(skb, vhdr); } } if (skb->protocol == htons(0x8809) || skb->protocol == htons(0x8902)) return 1; return 0; } #endif /*************************************************************************** * Function Name: smux_pkt_recv * Description : packet recv routine for all smux devices from real dev. * Returns : 0 on Success, 1 on drop, 2 on ignore ***************************************************************************/ int smux_pkt_recv(struct sk_buff *skb, struct net_device *dev) { struct smux_group *grp; unsigned char *dstAddr; struct sk_buff *skb2; struct smux_dev_info *dev_info; struct smux_dev_info *dev_info_first; struct list_head *lh; struct net_device *vdev; struct vlan_hdr *vhdr=NULL; unsigned short protocol=skb->protocol; int isTxDone = 0; if(!dev) { dev_kfree_skb(skb); return 1; } if(smux_ignore_check(skb)){ // printk("[%s:%d] match smux ignore rule, exit smux.\n", __FUNCTION__, __LINE__); return 2; } #if defined(CONFIG_RTK_8023AH) || defined(CONFIG_RTK_Y1731) // Ethernet OAM packet, not processed by smux if (smux_recv_eoam(skb)) return 2; // ignore and pass to eoam #endif grp = __find_smux_group(dev->name); if(!grp) { dev_kfree_skb(skb); return 1; } dstAddr = eth_hdr(skb)->h_dest; if (protocol == __constant_htons(ETH_P_8021Q)) { vhdr = (struct vlan_hdr *)(skb->data); protocol = vhdr->h_vlan_encapsulated_proto; } read_lock(&smux_lock); /* Multicast Traffic will go on all intf.*/ if (dstAddr[0] & 1) { dev_info_first = NULL; /* multicast or broadcast frames */ list_for_each(lh, &grp->virtual_devs) { dev_info = list_entry_smuxdev(lh); vdev = dev_info->vdev; #if defined(CONFIG_RTL_MULTI_ETH_WAN_MAC_BASED) #else if (!smux_check_vlan(dev_info, skb)) continue; #endif // do not check protocol here, it will be checked in linux protocol stack #if 0 if (((dev_info->proto == SMUX_PROTO_PPPOE) && (protocol != htons(ETH_P_PPP_DISC)) && (protocol != htons(ETH_P_PPP_SES))) || ((dev_info->proto == SMUX_PROTO_IPOE) && ((protocol == htons(ETH_P_PPP_DISC)) || (protocol == htons(ETH_P_PPP_SES))))) { DPRINTK("TRACE %d: packet dropped on RX dev %s\n", __LINE__, vdev->name); continue; } #endif if(!dev_info_first) { dev_info_first = dev_info; continue; } #ifdef CONFIG_PORT_MIRROR if(IN_NEED_MIR(dev_info->port_mirror)) { smux_mirror_pkt(skb, dev_info, IN); } #endif skb2 = skb_clone(skb, GFP_ATOMIC); if (skb2) { dev_info->stats.rx_packets++; dev_info->stats.rx_bytes += skb2->len; skb2->dev = vdev; skb2->from_dev = vdev; //skb2->pkt_type = PACKET_HOST; if (smux_eat_vlan(skb2, dev_info)) netif_rx(skb2); } } if (!dev_info_first) { dev_kfree_skb(skb); read_unlock(&smux_lock); return 1; } else { dev_info_first->stats.rx_packets++; dev_info_first->stats.rx_bytes += skb->len; skb->dev = dev_info_first->vdev; skb->from_dev = dev_info_first->vdev; //skb->pkt_type = PACKET_HOST; #ifdef CONFIG_PORT_MIRROR if(IN_NEED_MIR(dev_info->port_mirror)) { smux_mirror_pkt(skb, dev_info, IN); } #endif if (smux_eat_vlan(skb, dev_info_first)) netif_rx(skb); } isTxDone = 1; } else /* route Traffic.*/ { #ifndef UNIQUE_MAC_PER_DEV dev_info_first = NULL; #endif /* Routing Interface Traffic : check dst mac */ list_for_each(lh, &grp->virtual_devs) { dev_info = list_entry_smuxdev(lh); if (dev_info->proto == SMUX_PROTO_BRIDGE) continue; vdev = dev_info->vdev; #if defined(CONFIG_RTL_MULTI_ETH_WAN_MAC_BASED) if (!smux_check_mac(dev_info, skb)) continue; #else if (!smux_check_vlan(dev_info, skb)) continue; #endif #if 0 if (((dev_info->proto == SMUX_PROTO_PPPOE) && (protocol != htons(ETH_P_PPP_DISC)) && (protocol != htons(ETH_P_PPP_SES))) || ((dev_info->proto == SMUX_PROTO_IPOE) && ((protocol == htons(ETH_P_PPP_DISC)) || (protocol == htons(ETH_P_PPP_SES))))) { DPRINTK("TRACE %d: packet dropped on RX dev %s\n", __LINE__, vdev->name); continue; } #endif #ifndef UNIQUE_MAC_PER_DEV if (!memcmp(dstAddr, vdev->dev_addr, ETH_ALEN)) { if(!dev_info_first) { dev_info_first = dev_info; continue; } skb2 = skb_copy(skb, GFP_ATOMIC); skb2->dev = vdev; skb2->from_dev = vdev; dev_info->stats.rx_packets++; dev_info->stats.rx_bytes += skb2->len; skb2->pkt_type = PACKET_HOST; //printk("(route) receive from %s\n", vdev->name); #ifdef CONFIG_PORT_MIRROR if(IN_NEED_MIR(dev_info->port_mirror)) { smux_mirror_pkt(skb, dev_info, IN); } #endif if (smux_eat_vlan(skb2, dev_info)) netif_rx(skb2); isTxDone = 1; } #else if (!memcmp(dstAddr, vdev->dev_addr, ETH_ALEN)) { skb->dev = vdev; skb->from_dev = vdev; dev_info->stats.rx_packets++; dev_info->stats.rx_bytes += skb->len; skb->pkt_type = PACKET_HOST; //printk("(route) receive from %s\n", vdev->name); #ifdef CONFIG_PORT_MIRROR if(IN_NEED_MIR(dev_info->port_mirror)) { smux_mirror_pkt(skb, dev_info, IN); } #endif if (smux_eat_vlan(skb, dev_info)) netif_rx(skb); isTxDone = 1; break; } #endif } #ifndef UNIQUE_MAC_PER_DEV if (dev_info_first) { dev_info_first->stats.rx_packets++; dev_info_first->stats.rx_bytes += skb->len; skb->dev = dev_info_first->vdev; skb->from_dev = dev_info_first->vdev; skb->pkt_type = PACKET_HOST; //printk("(route) receive from %s\n", dev_info_first->vdev->name); #ifdef CONFIG_PORT_MIRROR if(IN_NEED_MIR(dev_info_first->port_mirror)) { smux_mirror_pkt(skb, dev_info_first, IN); } #endif if (smux_eat_vlan(skb, dev_info_first)) netif_rx(skb); isTxDone = 1; } #endif } if(isTxDone != 1) { /* Bridging Interface Traffic */ list_for_each(lh, &grp->virtual_devs) { dev_info = list_entry_smuxdev(lh); if (dev_info->proto != SMUX_PROTO_BRIDGE && !dev_info->brpppoe) continue; vdev = dev_info->vdev; #if defined(CONFIG_RTL_MULTI_ETH_WAN_MAC_BASED) #else if (!smux_check_vlan(dev_info, skb)) continue; #endif if (vdev->promiscuity) { skb->dev = vdev; skb->from_dev = vdev; dev_info->stats.rx_packets++; dev_info->stats.rx_bytes += skb->len; skb->pkt_type = PACKET_OTHERHOST; //printk("(bridge) receive from %s\n", vdev->name); #ifdef CONFIG_PORT_MIRROR if(IN_NEED_MIR(dev_info->port_mirror)) { smux_mirror_pkt(skb, dev_info, IN); } #endif if (smux_eat_vlan(skb, dev_info)) netif_rx(skb); isTxDone = 1; break; } } } read_unlock(&smux_lock); if(!isTxDone) { DPRINTK("dropping packet that has wrong dest. on RX dev %s\n", dev->name); dev_kfree_skb(skb); return 1; } return 0; } /* smux_pkt_recv */ /*Start:add by caoxiafei cKF24361 20100426 for fastpath*/ /*************************************************************************** * Function Name: getSmuxDev * Description : fetch smux devices according to real dev. * Returns : 0 on fail ***************************************************************************/ struct net_device * getSmuxDev(struct sk_buff *skb) { struct smux_group *grp; unsigned char *dstAddr; struct smux_dev_info *dev_info; struct list_head *lh; struct net_device *vdev; grp = __find_smux_group(skb->dev->name); if (!grp) return NULL; dstAddr = eth_hdr(skb)->h_dest; if (dstAddr[0]&0x01) return NULL; read_lock(&smux_lock); list_for_each(lh, &grp->virtual_devs) { dev_info = list_entry_smuxdev(lh); vdev = dev_info->vdev; if (ether_addr_equal(dstAddr, vdev->dev_addr)) { skb->dev = vdev; read_unlock(&smux_lock); return vdev; } } read_unlock(&smux_lock); return NULL; } /*End:add by caoxiafei cKF24361 20100426 for fastpath*/ /*************************************************************************** * Function Name: smux_dev_hard_start_xmit * Description : xmit routine for all smux devices on real dev. * Returns : 0 on Success ***************************************************************************/ __IRAM int smux_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct net_device_stats *stats = smux_dev_get_stats(dev); struct smux_dev_info *dev_info; struct netdev_queue *txq; dev_info = SMUX_DEV_INFO(dev); skb->dev = dev_info->smux_grp->real_dev; if (-1 == dev_info->vid) { #if CONFIG_RTL_8676HWNAT if (dev_info->proto == SMUX_PROTO_BRIDGE) skb->vlan_tci = RTL_BridgeWANVLANID; else skb->vlan_tci = RTL_WANVLANID; #else skb->vlan_tci = 0; #endif } else { if(dev_info->m_1p==0) skb->vlan_tci = (dev_info->vid&VLAN_VID_MASK)|VLAN_TAG_PRESENT; else { skb->vlan_tci = ((dev_info->vid&VLAN_VID_MASK)|((dev_info->m_1p-1)<<13))|VLAN_TAG_PRESENT; } } skb->vlan_member = dev_info->member; #ifdef CONFIG_PORT_MIRROR if((OUT_NEED_MIR(dev_info->port_mirror))) { smux_mirror_pkt(skb, dev_info, OUT); } #endif //printk("%s,%d::dev_info->member: %x\n",__func__,__LINE__,skb->vlan_member); if (!netif_queue_stopped(skb->dev)) { stats->tx_packets++; stats->tx_bytes += skb->len; //update real dev's txq trans txq = netdev_pick_tx(skb->dev, skb, NULL); txq->trans_start = jiffies; skb->dev->netdev_ops->ndo_start_xmit(skb, skb->dev); } else { netif_stop_queue(dev); dev_kfree_skb_any(skb); stats->tx_dropped++; } //dev_queue_xmit(skb); return 0; } /* smux_dev_hard_start_xmit */ /*************************************************************************** * Function Name: smux_dev_open * Description : * Returns : 0 on Success ***************************************************************************/ int smux_dev_open(struct net_device *vdev) { if (!(SMUX_DEV_INFO(vdev)->smux_grp->real_dev->flags & IFF_UP)) return -ENETDOWN; return 0; } /* smux_dev_open */ /*************************************************************************** * Function Name: smux_dev_stop * Description : * Returns : 0 on Success ***************************************************************************/ int smux_dev_stop(struct net_device *dev) { return 0; } /* smux_dev_stop */ /*************************************************************************** * Function Name: smux_dev_set_mac_address * Description : sets the mac for devs * Returns : 0 on Success ***************************************************************************/ int smux_dev_set_mac_address(struct net_device *dev, void *addr_struct_p) { struct sockaddr *addr = (struct sockaddr *)(addr_struct_p); int i, flgs; #if 0 #ifdef CONFIG_RTL_8676HWNAT rtl865x_netif_t netif; #endif #endif #ifdef UNIQUE_MAC_PER_DEV struct smux_group *grp = NULL; struct smux_dev_info *vdev_info = NULL; struct list_head *lh; #endif if (netif_running(dev)) return -EBUSY; memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); memset(dev->broadcast, 0xff, ETH_ALEN); #if defined(CONFIG_RTL_HW_NAPT) && defined(CONFIG_RTL_FLOW_BASE_HWNAT) //memcpy(netif.macAddr.octet,addr->sa_data,ETHER_ADDR_LEN); //memcpy(netif.name,dev->name,MAX_IFNAMESIZE); //rtl865x_setNetifMac(&netif); rtl8676_set_Multiwan_NetifMacAddr(dev->name, addr->sa_data); #endif #if defined(CONFIG_RTL8685_PTM_MII) && defined(USE_PTM_MII_WANIF) if(is_ptmdev(dev)) ptm_mii_update_wanif_macaddr(dev->name, addr->sa_data); #endif /*USE_PTM_MII_WANIF*/ return 0; #if 0 #ifdef UNIQUE_MAC_PER_DEV grp = __find_smux_group(ALIASNAME_NAS0); //grp = __find_smux_group("nas0"); if (!grp) return -EADDRNOTAVAIL; if (list_empty(&grp->virtual_devs)) { return -EADDRNOTAVAIL; } list_for_each(lh, &grp->virtual_devs) { vdev_info = list_entry_smuxdev(lh); if (vdev_info->vdev == dev) continue; if (!memcmp(vdev_info->vdev->dev_addr, addr->sa_data, ETH_ALEN)) { return -EADDRNOTAVAIL; } } #endif memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); printk("%s: Setting MAC address to ", dev->name); for (i = 0; i < 6; i++) printk(" %2.2x", dev->dev_addr[i]); printk(".\n"); if (memcmp(SMUX_DEV_INFO(dev)->smux_grp->real_dev->dev_addr, dev->dev_addr, dev->addr_len) != 0) { if (!(SMUX_DEV_INFO(dev)->smux_grp->real_dev->flags & IFF_PROMISC)) { flgs = SMUX_DEV_INFO(dev)->smux_grp->real_dev->flags; /* Increment our in-use promiscuity counter */ dev_set_promiscuity(SMUX_DEV_INFO(dev)->smux_grp->real_dev, 1); /* Make PROMISC visible to the user. */ flgs |= IFF_PROMISC; printk("SMUX (%s): Setting underlying device (%s) to promiscious mode.\n", dev->name, SMUX_DEV_INFO(dev)->smux_grp->real_dev->name); dev_change_flags(SMUX_DEV_INFO(dev)->smux_grp->real_dev, flgs); } } else { printk("SMUX (%s): Underlying device (%s) has same MAC, not checking promiscious mode.\n", dev->name, SMUX_DEV_INFO(dev)->smux_grp->real_dev->name); } SMUX_DEV_INFO(dev)->smux_grp->real_dev->netdev_ops->ndo_set_mac_address(dev, addr_struct_p); return 0; #endif } /* smux_dev_set_mac_address */ #ifdef CONFIG_PORT_MIRROR static int smux_do_mirror(struct net_device *dev, struct portmir *pmr) { struct smux_dev_info *dev_info = SMUX_DEV_INFO(dev); switch (pmr->port_mirror) { case -1: /* status */ printk("%s:\taction=%d, todev=%s\n", dev->name, dev_info->port_mirror, dev_info->mirror_dev?dev_info->mirror_dev->name:"n/a"); break; case -2: /* flush */ dev_info->port_mirror = 0; dev_info->mirror_dev = 0; break; default: printk("%s: error command!\n", __FUNCTION__); } return 0; } #endif /*************************************************************************** * Function Name: smux_dev_ioctl * Description : handles device related ioctls * Returns : 0 on Success ***************************************************************************/ int smux_dev_ioctl(struct net_device *vdev, struct ifreq *ifr, int cmd) { struct net_device *real_dev = SMUX_DEV_INFO(vdev)->smux_grp->real_dev; struct ifreq ifrr; int err = -EOPNOTSUPP; #ifdef CONFIG_RTL_8676HWNAT extern int set_port_mapping(struct net_device *dev, struct ifreq *rq, int cmd); #endif strncpy(ifrr.ifr_name, real_dev->name, IFNAMSIZ); ifrr.ifr_ifru = ifr->ifr_ifru; //printk("%s %d cmd 0x%x (dev:%s)\n", __func__, __LINE__, cmd,vdev->name); switch(cmd) { case SIOCGMIIPHY: case SIOCGMIIREG: case SIOCSMIIREG: if (real_dev->netdev_ops->ndo_do_ioctl && netif_device_present(real_dev)) err = real_dev->netdev_ops->ndo_do_ioctl(real_dev, &ifrr, cmd); break; case SIOCETHTOOL: err = dev_ethtool(&init_net, &ifrr); if (!err) ifr->ifr_ifru = ifrr.ifr_ifru; break; #ifdef CONFIG_PORT_MIRROR #define SIOCPORTMIRROR (SIOCDEVPRIVATE + 0xf) /*ethwan port mirror*/ case SIOCPORTMIRROR: { struct portmir *pmr; struct smux_dev_info *dev_info = SMUX_DEV_INFO(vdev); struct net_device *dev = NULL; pmr = (struct portmir *)ifr->ifr_data; if (pmr->port_mirror < 0) { smux_do_mirror(vdev, pmr); err = 0; break; } dev = dev_get_by_name(&init_net, pmr->mir_dev_name); if(!dev) { printk("error lan device!\n"); break; } if((dev->priv_flags & IFF_DOMAIN_ELAN) == 0) { printk("error lan device!\n"); break; } dev_info->port_mirror = pmr->port_mirror; dev_info->mirror_dev = dev; err = 0; break; } #endif case SIOCSITFGROUP: { struct ifvlan *ifvl; struct smux_dev_info *dev_info = SMUX_DEV_INFO(vdev); ifvl = (struct ifvlan *)ifr->ifr_data; if (ifvl->enable) dev_info->member = ifvl->member; else dev_info->member = 0xFFFFFFFF; printk("%s %d set portmapping (dev:%s member:0x%X) \n", __func__, __LINE__,vdev->name,dev_info->member); #ifdef CONFIG_RTL_8676HWNAT err = set_port_mapping(vdev, ifr, cmd); #endif #if defined(CONFIG_RTL_HW_NAPT) && defined(CONFIG_RTL_FLOW_BASE_HWNAT) rtl8676_update_portmapping_Multiwan_dev(vdev->name,dev_info->member); #endif } //rtl865x_updateNetifForPortmapping(); return err; #if defined(CONFIG_RTL8685_PTM_MII) && defined(USE_PTM_MII_WANIF) case SIOCSPTMQMAP: { unsigned char *p; p=ifr->ifr_data; printk( "SIOCSPTMQMAP, dev=%s, %x\n", vdev->name, (unsigned int)p ); if(p!=NULL) printk( "path=%u, qmap=%u%u%u%u%u%u%u%u\n", p[0],p[1],p[2],p[3],p[4] ,p[5],p[6],p[7],p[8]); if(is_ptmdev(vdev) && p!=NULL) err=ptm_mii_update_qmap( vdev->name, p[0], &p[1] ); return err; } #endif /*USE_PTM_MII_WANIF*/ } return err; } /* smux_dev_ioctl */ /*************************************************************************** * Function Name: smux_dev_change_mtu * Description : changes mtu for dev * Returns : 0 on Success ***************************************************************************/ int smux_dev_change_mtu(struct net_device *vdev, int new_mtu) { //MTU should be larger than real device. if (SMUX_DEV_INFO(vdev)->smux_grp->real_dev->mtu < new_mtu) return -ERANGE; //vdev->mtu = new_mtu; SMUX_DEV_INFO(vdev)->smux_grp->real_dev->netdev_ops->ndo_change_mtu(vdev, new_mtu); return 0; } /*************************************************************************** * Function Name: smux_setup * Description : inits device api * Returns : None ***************************************************************************/ static void smux_setup(struct net_device *new_dev) { SET_MODULE_OWNER(new_dev); /* Make this thing known as a SMUX device */ new_dev->priv_flags |= IFF_OSMUX; new_dev->tx_queue_len = 0; #ifdef CONFIG_COMPAT_NET_DEV_OPS /* set up method calls */ new_dev->change_mtu = smux_dev_change_mtu; new_dev->open = smux_dev_open; new_dev->stop = smux_dev_stop; new_dev->set_mac_address = smux_dev_set_mac_address; /*new_dev->set_multicast_list = smux_dev_set_multicast_list; TODO: */ new_dev->destructor = free_netdev; new_dev->do_ioctl = smux_dev_ioctl; new_dev->get_stats = smux_dev_get_stats; #endif } /* smux_setup */ /*************************************************************************** * Function Name: smux_transfer_operstate * Description : updates the operstate of overlay device * Returns : None. ***************************************************************************/ static void smux_transfer_operstate(const struct net_device *rdev, struct net_device *vdev) { if (rdev->operstate == IF_OPER_DORMANT) netif_dormant_on(vdev); else netif_dormant_off(vdev); if (netif_carrier_ok(rdev)) { if (!netif_carrier_ok(vdev)) netif_carrier_on(vdev); } else { if (netif_carrier_ok(vdev)) netif_carrier_off(vdev); } } /* smux_transfer_operstate */ static const struct ethtool_ops smux_ethtool_ops = { .get_link = ethtool_op_get_link, }; #if defined(CONFIG_COMPAT_NET_DEV_OPS) #else static const struct net_device_ops smux_netdev_ops = { .ndo_open = smux_dev_open, .ndo_stop = smux_dev_stop, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = smux_dev_set_mac_address, .ndo_do_ioctl = smux_dev_ioctl, .ndo_start_xmit = smux_dev_hard_start_xmit, .ndo_change_mtu = smux_dev_change_mtu, .ndo_get_stats = smux_dev_get_stats, }; #endif /*************************************************************************** * Function Name: smux_register_device * Description : regists new overlay device on real device & registers for packet handlers depending on the protocol types * Returns : 0 on Success ***************************************************************************/ static struct net_device *smux_register_device(const char *rifname, const char *nifname, int smux_proto, int vid, int napt, int brpppoe) { struct net_device *new_dev = NULL; struct net_device *real_dev = NULL; struct smux_group *grp = NULL; struct smux_dev_info *vdev_info = NULL; //int mac_reused = 0; //unsigned char LSB=0; //struct list_head *lh; real_dev = dev_get_by_name(&init_net, rifname); if (!real_dev) { goto real_dev_invalid; } if (!(real_dev->flags & IFF_UP)) { goto real_dev_invalid; } new_dev = alloc_netdev(sizeof(struct smux_dev_info), nifname, NET_NAME_USER, smux_setup); if (new_dev == NULL) { printk("netdev alloc failure\n"); goto new_dev_invalid; } else { new_dev->priv = netdev_priv(new_dev); } //dev->netdev_ops = &rtl819x_netdev_ops; ether_setup(new_dev); if (vid != -1) new_dev->priv_flags |= IFF_VSMUX; new_dev->flags &= ~IFF_UP; new_dev->flags &= ~IFF_MULTICAST; new_dev->priv_flags |= IFF_DOMAIN_WAN; #if 0 #if (defined(CONFIG_RTL_ETH_TX_SG) || defined(CONFIG_RTL_GSO)) new_dev->features |= (NETIF_F_GSO | NETIF_F_SG); #endif #if (defined(CONFIG_RTL_HW_TX_CSUM)) new_dev->features |= NETIF_F_HW_CSUM; #endif #if defined(CONFIG_RTL_TSO) new_dev->features |= (NETIF_F_TSO | NETIF_F_TSO_ECN); #endif #else new_dev->features = real_dev->features; #endif real_dev->priv_flags |= IFF_RSMUX; new_dev->state = (real_dev->state & ((1<<__LINK_STATE_NOCARRIER) | (1<<__LINK_STATE_DORMANT))) | (1<<__LINK_STATE_PRESENT); new_dev->mtu = real_dev->mtu; new_dev->type = real_dev->type; new_dev->hard_header_len = real_dev->hard_header_len; #ifdef CONFIG_COMPAT_NET_DEV_OPS new_dev->hard_start_xmit = smux_dev_hard_start_xmit; new_dev->set_mac_address = smux_dev_set_mac_address; #else new_dev->netdev_ops = &smux_netdev_ops; #endif new_dev->ethtool_ops = &smux_ethtool_ops; /* find smux group name. if not found create all new smux group */ grp = __find_smux_group(rifname); if (!grp) { grp = kzalloc(sizeof(struct smux_group), GFP_KERNEL); if(grp) { INIT_LIST_HEAD(&grp->virtual_devs); INIT_LIST_HEAD(&grp->smux_grp_devs); grp->real_dev = real_dev; write_lock_irq(&smux_lock); list_add_tail(&grp->smux_grp_devs, &smux_grp_devs); write_unlock_irq(&smux_lock); } else { printk("%s %d create smux group failed\n", __func__, __LINE__); free_netdev(new_dev); new_dev = NULL; } } if(grp && new_dev) { /* Assign default mac to bridge so that we can add it to linux bridge */ if(smux_proto == SMUX_PROTO_BRIDGE) { memcpy( new_dev->dev_addr, "\xFE\xFF\xFF\xFF\xFF\xFF", ETH_ALEN ); } else { #ifdef UNIQUE_MAC_PER_DEV if (list_empty(&grp->virtual_devs)) { memcpy(new_dev->dev_addr, real_dev->dev_addr, ETH_ALEN); } else { list_for_each(lh, &grp->virtual_devs) { vdev_info = list_entry_smuxdev(lh); if (!memcmp(real_dev->dev_addr, vdev_info->vdev->dev_addr, ETH_ALEN)) { mac_reused = 1; } if (LSB < vdev_info->vdev->dev_addr[5]) LSB = vdev_info->vdev->dev_addr[5]; } memcpy(new_dev->dev_addr, real_dev->dev_addr, ETH_ALEN); if (mac_reused) { //generate new mac address, real_addr mac addr increased by 1. new_dev->dev_addr[5] = LSB+1; } } #else memcpy(new_dev->dev_addr, real_dev->dev_addr, ETH_ALEN); #endif } } if(grp && new_dev) { struct net_device *ret_dev; /*find new smux in smux group if it does not exit create one*/ if(NULL == (ret_dev=__find_smux_in_smux_group(grp, nifname))) { vdev_info = SMUX_DEV_INFO(new_dev); if (!vdev_info) { free_netdev(new_dev); new_dev = NULL; printk("%s(%d) vdev_info is NULL\n", __func__, __LINE__); } else { memset(vdev_info, 0, sizeof(struct smux_dev_info)); //m_1p : 0~8, 0 is meaning disable if(vid>=0) vdev_info->m_1p=vid>>13; else vdev_info->m_1p=0; vdev_info->smux_grp = grp; vdev_info->vdev = new_dev; vdev_info->proto = smux_proto; #ifdef UNIQUE_MAC_PER_DEV if ((vid == -1) && (smux_proto != SMUX_PROTO_BRIDGE)) { if ((vid = allocSmuxDevVid()) == -1) printk("fatal error, too many wan interface created.\n"); } #endif if(vid!=-1) vdev_info->vid = (vid&VLAN_VID_MASK); else vdev_info->vid = vid; vdev_info->napt = napt; vdev_info->brpppoe = brpppoe; vdev_info->member = 0xFFFFFFFF; //init membership to include all interface. #ifdef CONFIG_PORT_MIRROR vdev_info->port_mirror = 0; vdev_info->mirror_dev = NULL; #endif if(smux_proto == SMUX_PROTO_BRIDGE) { new_dev->promiscuity = 1; } else if(smux_proto == SMUX_PROTO_IPOE) { new_dev->flags |= IFF_MULTICAST; } if (register_netdev(new_dev)) { printk("register_netdev failed\n"); //list_del(&vdev_info->list); free_netdev(new_dev); new_dev = NULL; } else { INIT_LIST_HEAD(&vdev_info->list); write_lock_irq(&smux_lock); /* To avoid vlan-tagged packet recognize incorrectly vlan-untagged WAN device due to vlanpassthu, we add vlan-tagged WAN device after list head and vlan-untagged WAN device to list tail. */ if(vid!=-1) list_add(&vdev_info->list, &grp->virtual_devs); else list_add_tail(&vdev_info->list, &grp->virtual_devs); write_unlock_irq(&smux_lock); smux_transfer_operstate(real_dev, new_dev); } #ifdef CONFIG_RTL_HW_NAPT if(new_dev) { if(smux_proto != SMUX_PROTO_BRIDGE && brpppoe) { char ifname_extend[IFNAMSIZ]; strcpy(ifname_extend,vdev_info->vdev->name); strcat(ifname_extend,"_B"); #if defined(CONFIG_RTL_FLOW_BASE_HWNAT) && !defined(CONFIG_RTL_EVENT_TRIGGER_HWNAT) rtl8676_register_Multiwan_dev(vdev_info->vdev->name, smux_proto, vid, napt); rtl8676_register_Multiwan_dev(ifname_extend, SMUX_PROTO_BRIDGE, vid, napt); #endif } else { #if defined(CONFIG_RTL_FLOW_BASE_HWNAT) && !defined(CONFIG_RTL_EVENT_TRIGGER_HWNAT) rtl8676_register_Multiwan_dev(vdev_info->vdev->name, smux_proto, vid, napt); #endif } } #endif #if defined(CONFIG_RTL8685_PTM_MII) && defined(USE_PTM_MII_WANIF) if(new_dev) { if(is_ptmdev(new_dev)) ptm_mii_register_wanif( new_dev->name, new_dev->dev_addr, -1, vdev_info->vid, vdev_info->proto==SMUX_PROTO_BRIDGE ); } #endif /*USE_PTM_MII_WANIF*/ } } else { printk("device %s already exist.\n", nifname); free_netdev(new_dev); new_dev = ret_dev; } } return new_dev; real_dev_invalid: new_dev_invalid: return NULL; } /* smux_register_device */ /*************************************************************************** * Function Name: smux_unregister_device * Description : unregisters the smux devices along with releasing mem. * Returns : 0 on Success ***************************************************************************/ static int smux_unregister_device(const char* vifname) { struct net_device *vdev = NULL; struct net_device *real_dev = NULL; int ret; struct smux_dev_info *dev_info; ret = -EINVAL; vdev = dev_get_by_name(&init_net, vifname); if(vdev) { printk("%s remove smux dev %s\n", __func__, vifname); /* remove related acl rule */ #if 0 #ifdef CONFIG_RTL8676_Dynamic_ACL rtl865x_acl_control_delete_all_by_netif(vdev->name); #endif rtl865x_delNetif(vdev->name); #endif dev_info = SMUX_DEV_INFO(vdev); #if defined(CONFIG_RTL_HW_NAPT) && !defined(CONFIG_RTL_EVENT_TRIGGER_HWNAT) rtl8676_unregister_Multiwan_dev(vdev->name); { char ifname_extend[IFNAMSIZ]; strcpy(ifname_extend,vdev->name); strcat(ifname_extend,"_B"); if(rtl865x_netifExist(ifname_extend)) rtl865x_unregisterDev(ifname_extend); } #endif #if defined(CONFIG_RTL8685_PTM_MII) && defined(USE_PTM_MII_WANIF) if(is_ptmdev(vdev)) ptm_mii_unregister_wanif( vdev->name ); #endif /*USE_PTM_MII_WANIF*/ #ifdef UNIQUE_MAC_PER_DEV freeSmuxDevVid(dev_info->vid); #endif real_dev = dev_info->smux_grp->real_dev; write_lock_irq(&smux_lock); list_del(&dev_info->list); write_unlock_irq(&smux_lock); if (list_empty(&dev_info->smux_grp->virtual_devs)) { write_lock_irq(&smux_lock); list_del(&dev_info->smux_grp->smux_grp_devs); write_unlock_irq(&smux_lock); kfree(dev_info->smux_grp); } dev_put(vdev); unregister_netdev(vdev); free_netdev(vdev); synchronize_net(); dev_put(real_dev); ret = 0; } return ret; } /* smux_unregister_device */ /*************************************************************************** * Function Name: smux_device_event * Description : handles real device events to update overlay devs. status * Returns : 0 on Success ***************************************************************************/ static int smux_device_event(struct notifier_block *unused, unsigned long event, void *ptr) { struct net_device *rdev = netdev_notifier_info_to_dev(ptr); struct smux_group *grp = __find_smux_group(rdev->name); int flgs; struct list_head *lh; struct list_head *lhp; struct smux_dev_info *dev_info; if (!grp) goto out; switch (event) { case NETDEV_CHANGE: /* Propagate real device state to overlay devices */ read_lock(&smux_lock); list_for_each(lh, &grp->virtual_devs) { dev_info = list_entry_smuxdev(lh); if(dev_info) { smux_transfer_operstate(rdev, dev_info->vdev); } } read_unlock(&smux_lock); break; case NETDEV_DOWN: /* Put all Overlay devices for this dev in the down state too.*/ read_lock(&smux_lock); list_for_each(lh, &grp->virtual_devs) { dev_info = list_entry_smuxdev(lh); if(dev_info) { flgs = dev_info->vdev->flags; if (!(flgs & IFF_UP)) continue; dev_change_flags(dev_info->vdev, flgs & ~IFF_UP); } } read_unlock(&smux_lock); break; case NETDEV_UP: /* Put all Overlay devices for this dev in the up state too. */ read_lock(&smux_lock); list_for_each(lh, &grp->virtual_devs) { dev_info = list_entry_smuxdev(lh); if(dev_info) { smux_transfer_operstate(rdev, dev_info->vdev); flgs = dev_info->vdev->flags; if (flgs & IFF_UP) continue; dev_change_flags(dev_info->vdev, flgs & IFF_UP); } } read_unlock(&smux_lock); break; case NETDEV_UNREGISTER: /* Delete all Overlay devices for this dev. */ write_lock_irq(&smux_lock); list_for_each_safe(lh, lhp, &grp->virtual_devs) { dev_info = list_entry_smuxdev(lh); if(dev_info) { /* delete by l67530 for cpu0 when reboot system. HG551c.2010/12/07 */ //list_del(&dev_info->list); smux_unregister_device(dev_info->vdev->name); } } write_unlock_irq(&smux_lock); break; } out: return NOTIFY_DONE; } /* smux_device_event */ /*************************************************************************** * Function Name: smux_ioctl_handler * Description : ioctl handler for user apps * Returns : 0 on Success ***************************************************************************/ static int smux_ioctl_handler(void __user *arg) { int err = 0; struct smux_ioctl_args args; SMUX_IGN_ENTRY *pEntry,*next, tmpEntry; if (copy_from_user(&args, arg, sizeof(struct smux_ioctl_args))) return -EFAULT; args.rsmux_ifname[IFNAMSIZ-1] = 0; args.osmux_ifname[IFNAMSIZ-1] = 0; switch (args.cmd) { case ADD_SMUX_CMD: if (!capable(CAP_NET_ADMIN)) return -EPERM; if(smux_register_device(args.rsmux_ifname, args.osmux_ifname, args.proto, args.vid, args.napt, args.brpppoe)) { err = 0; } else { err = -EINVAL; } break; case REM_SMUX_CMD: if (!capable(CAP_NET_ADMIN)) return -EPERM; err = smux_unregister_device(args.u.ifname); break; case IGN_SMUX_CMD: if(args.vid == 0){ /* dump ignore list */ list_for_each_entry_safe(pEntry, next, &SMUX_IGNORE_HEAD, smuxIgnoreList){ printk(" proto=0x%x\n", pEntry->proto); } }else if((args.vid == 1) || (args.vid == 2)){ /* 1: add ignore list */ /* 2: del ignore list */ if(!list_empty(&SMUX_IGNORE_HEAD)){ list_for_each_entry_safe(pEntry, next, &SMUX_IGNORE_HEAD, smuxIgnoreList){ if(pEntry->proto == args.proto){ if(args.vid == 2){ list_del(&pEntry->smuxIgnoreList); kfree(pEntry); } break; } } } if(args.vid == 1){ pEntry = (SMUX_IGN_ENTRY*)kmalloc(sizeof(SMUX_IGN_ENTRY), GFP_KERNEL); //it should free when del from list memset(pEntry, 0, sizeof(SMUX_IGN_ENTRY)); if(args.proto <= 0xFFFF){ pEntry->proto = args.proto; list_add_tail(&pEntry->smuxIgnoreList, &SMUX_IGNORE_HEAD); } } } break; default: printk("%s: Unknown SMUX CMD: %x \n", __FUNCTION__, args.cmd); return -EINVAL; } return err; } /* smux_ioctl_handler */ static int vlan_passthru_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) { unsigned char chartmp; if (count > 1){ if (buffer && !copy_from_user(&chartmp, buffer, 1)){ vlan_passthru = chartmp - '0'; printk("vlan passthru : %s\n", vlan_passthru ? "Enable" : "Disable"); } } else { printk("write fail\n"); return -EFAULT; } return count; } static int vlan_passthru_read_proc(struct seq_file *f, void *data) { seq_printf(f, "vlan passthru : %s\n", vlan_passthru ? "Enable" : "Disable"); return 0; } static int read_proc_open_vlan_passthru(struct inode *inode, struct file *file) { return(single_open(file, vlan_passthru_read_proc, NULL)); } static ssize_t write_proc_vlan_passthru(struct file *file, const char __user * userbuf, size_t count, loff_t * off) { return vlan_passthru_write_proc(file, userbuf, count, NULL); } static struct file_operations fops_proc_vlan_passthru = { .open = read_proc_open_vlan_passthru, .read = seq_read, .llseek = seq_lseek, .release = single_release, .write = write_proc_vlan_passthru, }; extern struct proc_dir_entry *realtek_proc; static struct proc_dir_entry *vlan_passthru_proc=NULL; static int initVlanPassThruProc(void) { vlan_passthru_proc = proc_create_data("vlan_passthru", 0644, realtek_proc, &fops_proc_vlan_passthru, NULL); if (!vlan_passthru_proc) { printk("Realtek SMUX VLan PassThru, create proc failed!\n"); } return 0; } /*************************************************************************** * Function Name: smux_drv_init * Description : Initialization of smux driver * Returns : struct net_device ***************************************************************************/ static int __init smux_drv_init(void) { register_netdevice_notifier(&smux_notifier_block); smux_ioctl_set(smux_ioctl_handler); initVlanPassThruProc(); INIT_LIST_HEAD(&SMUX_IGNORE_HEAD); return 0; } /* smux_drv_init */ /*************************************************************************** * Function Name: smux_cleanup_devices * Description : cleans up all the smux devices and releases memory on exit * Returns : None ***************************************************************************/ static void __exit smux_cleanup_devices(void) { struct net_device *dev; struct list_head *lh; struct list_head *lhp; struct smux_dev_info *dev_info; struct smux_group *grp; /* clean up all the smux devices */ rtnl_lock(); for_each_netdev(&init_net, dev) { if (dev->priv_flags & IFF_OSMUX) { dev_info = SMUX_DEV_INFO(dev); write_lock_irq(&smux_lock); list_del(&dev_info->list); write_unlock_irq(&smux_lock); unregister_netdevice(dev); } } rtnl_unlock(); /* cleanup all smux groups */ write_lock_irq(&smux_lock); list_for_each_safe(lh, lhp, &smux_grp_devs) { grp = list_entry_smuxgrp(lh); if(grp) { list_del(&grp->virtual_devs); } } write_unlock_irq(&smux_lock); } /* smux_cleanup_devices */ /*************************************************************************** * Function Name: smux_drv_exit * Description : smux module clean routine * Returns : None ***************************************************************************/ static void __exit smux_drv_exit(void) { smux_ioctl_set(NULL); /* Un-register us from receiving netdevice events */ unregister_netdevice_notifier(&smux_notifier_block); smux_cleanup_devices(); synchronize_net(); } /* smux_drv_exit */ struct net_device* getSmuxRealDev(struct net_device *vdev) { struct smux_dev_info *dev_info; if(vdev==NULL) return NULL; dev_info = SMUX_DEV_INFO(vdev); if(dev_info && dev_info->smux_grp && dev_info->smux_grp->real_dev) { return dev_info->smux_grp->real_dev; } return NULL; } int getVidOfSmuxDev(char *name) { struct smux_dev_info *dev_info; struct net_device *vdev; vdev = dev_get_by_name(&init_net, name); if (!vdev) return -1; dev_put(vdev); dev_info = SMUX_DEV_INFO(vdev); if (dev_info->vid > 0 && dev_info->vid < 4096) { return dev_info->vid; } else { if (dev_info->proto == SMUX_PROTO_BRIDGE) return RTL_BridgeWANVLANID; else return RTL_WANVLANID; } } #if 0 /*move to rtl_nic.c*/ unsigned int getMemberOfSmuxDevByVid(unsigned int vid) { struct smux_dev_info *dev_info; struct net_device *vdev; unsigned char name[MAX_IFNAMESIZE]; rtl865x_getMasterNetifByVid(vid, name); vdev = dev_get_by_name(&init_net, name); if (!vdev) return -1; dev_put(vdev); dev_info = SMUX_DEV_INFO(vdev); return dev_info->member; } #endif /*attention: curridx>=11 is reserved for ppp interface*/ #if 0 int getVlanconfigIdx(struct rtl865x_vlanConfig *vconfig, int *curridx, int vid) #endif #if 0 int getVlanconfigIdx(struct rtl865x_vlanConfig *vconfig, int vid) { unsigned int i; #if 0 if (vconfig[1].vid && (vid == vconfig[1].vid))//get { if (*curridx == 0) *curridx = 1; return 1; } for (i=5; (i<=*curridx) && (i<=10); i++) { if (vconfig[i].vid && (vconfig[i].vid == vid))//get return i; } *curridx = (*curridx==1)?5:(*curridx+1); return (*curridx); #else for(i=0; vconfig[i].ifname[0] != '\0' ; i++) { //if( vconfig[i].vid && vconfig[i].isWan==1 && vconfig[i].is_slave==0) if( vconfig[i].vid && vconfig[i].is_slave==0) { if(vconfig[i].vid == vid) return i; } } for(i=0; vconfig[i].ifname[0] != '\0' ; i++) { //if( !vconfig[i].vid && vconfig[i].isWan==1 && vconfig[i].is_slave==0) if( !vconfig[i].vid && vconfig[i].is_slave==0) return i; } return -1; #endif } #endif /* vconfig: vlanconfig */ /* * BRIDGE: vlan enable vid=CUSTOM_VAL * vlan disable vid=RTL_LANVLANID * ROUTE: vlan enable vid=CUSTOM_VAL * vlan disable vid=RTL_LANVLANID */ #if 0 void rtl_adjust_wanport_vlanconfig(struct rtl865x_vlanConfig *vconfig,int *curr_idx) { struct smux_group *grp; struct list_head *lh; struct smux_dev_info * sdev = NULL; //int vid=1; /* vlanconfig index 1,5-10 is for wan interface. */ #if 0 int idx=0, curridx=0; #else int idx=0; #endif #ifdef CONFIG_RTL_ALIASNAME grp = __find_smux_group(ALIASNAME_NAS0); #else grp = __find_smux_group("nas0"); #endif if (!grp) return; /*init vconfig firstly */ #if 0 vconfig[1].vid = 0; vconfig[1].untagSet = RTL_WANPORT_MASK; vconfig[1].memPort = RTL_WANPORT_MASK; for (idx=5; idx<11; idx++) { vconfig[idx].vid = 0; vconfig[idx].untagSet = RTL_WANPORT_MASK; vconfig[idx].memPort = RTL_WANPORT_MASK; } #else //strcpy(vconfig[*curr_idx].ifname,RTL_DRV_WAN0_NETIF_NAME); vconfig[*curr_idx].isWan = 1; vconfig[*curr_idx].if_type = IF_ETHER; vconfig[*curr_idx].vid = 0; vconfig[*curr_idx].fid = RTL_WAN_FID; vconfig[*curr_idx].memPort = RTL_WANPORT_MASK; vconfig[*curr_idx].untagSet = RTL_WANPORT_MASK; vconfig[*curr_idx].is_slave = 0; (*curr_idx)++; //strcpy(vconfig[*curr_idx].ifname,RTL_DRV_WAN1_NETIF_NAME); vconfig[*curr_idx].isWan = 1; vconfig[*curr_idx].if_type = IF_ETHER; vconfig[*curr_idx].vid = 0; vconfig[*curr_idx].fid = RTL_WAN_FID; vconfig[*curr_idx].memPort = RTL_WANPORT_MASK; vconfig[*curr_idx].untagSet = RTL_WANPORT_MASK; vconfig[*curr_idx].is_slave = 0; (*curr_idx)++; //strcpy(vconfig[*curr_idx].ifname,RTL_DRV_WAN2_NETIF_NAME); vconfig[*curr_idx].isWan = 1; vconfig[*curr_idx].if_type = IF_ETHER; vconfig[*curr_idx].vid = 0; vconfig[*curr_idx].fid = RTL_WAN_FID; vconfig[*curr_idx].memPort = RTL_WANPORT_MASK; vconfig[*curr_idx].untagSet = RTL_WANPORT_MASK; vconfig[*curr_idx].is_slave = 0; (*curr_idx)++; //strcpy(vconfig[*curr_idx].ifname,RTL_DRV_WAN3_NETIF_NAME); vconfig[*curr_idx].isWan = 1; vconfig[*curr_idx].if_type = IF_ETHER; vconfig[*curr_idx].vid = 0; vconfig[*curr_idx].fid = RTL_WAN_FID; vconfig[*curr_idx].memPort = RTL_WANPORT_MASK; vconfig[*curr_idx].untagSet = RTL_WANPORT_MASK; vconfig[*curr_idx].is_slave = 0; (*curr_idx)++; //strcpy(vconfig[*curr_idx].ifname,RTL_DRV_WAN4_NETIF_NAME); vconfig[*curr_idx].isWan = 1; vconfig[*curr_idx].if_type = IF_ETHER; vconfig[*curr_idx].vid = 0; vconfig[*curr_idx].fid = RTL_WAN_FID; vconfig[*curr_idx].memPort = RTL_WANPORT_MASK; vconfig[*curr_idx].untagSet = RTL_WANPORT_MASK; vconfig[*curr_idx].is_slave = 0; (*curr_idx)++; //strcpy(vconfig[*curr_idx].ifname,RTL_DRV_WAN5_NETIF_NAME); vconfig[*curr_idx].isWan = 1; vconfig[*curr_idx].if_type = IF_ETHER; vconfig[*curr_idx].vid = 0; vconfig[*curr_idx].fid = RTL_WAN_FID; vconfig[*curr_idx].memPort = RTL_WANPORT_MASK; vconfig[*curr_idx].untagSet = RTL_WANPORT_MASK; vconfig[*curr_idx].is_slave = 0; (*curr_idx)++; //strcpy(vconfig[*curr_idx].ifname,RTL_DRV_WAN6_NETIF_NAME); vconfig[*curr_idx].isWan = 1; vconfig[*curr_idx].if_type = IF_ETHER; vconfig[*curr_idx].vid = 0; vconfig[*curr_idx].fid = RTL_WAN_FID; vconfig[*curr_idx].memPort = RTL_WANPORT_MASK; vconfig[*curr_idx].untagSet = RTL_WANPORT_MASK; vconfig[*curr_idx].is_slave = 0; (*curr_idx)++; //strcpy(vconfig[*curr_idx].ifname,RTL_DRV_PPP0_NETIF_NAME); vconfig[*curr_idx].isWan = 1; vconfig[*curr_idx].if_type = IF_PPPOE; vconfig[*curr_idx].vid = RTL_WANVLANID; vconfig[*curr_idx].fid = RTL_WAN_FID; vconfig[*curr_idx].memPort = RTL_WANPORT_MASK; vconfig[*curr_idx].untagSet = RTL_WANPORT_MASK; vconfig[*curr_idx].is_slave = 1; (*curr_idx)++; //strcpy(vconfig[*curr_idx].ifname,RTL_DRV_PPP1_NETIF_NAME); vconfig[*curr_idx].isWan = 1; vconfig[*curr_idx].if_type = IF_PPPOE; vconfig[*curr_idx].vid = RTL_WANVLANID; vconfig[*curr_idx].fid = RTL_WAN_FID; vconfig[*curr_idx].memPort = RTL_WANPORT_MASK; vconfig[*curr_idx].untagSet = RTL_WANPORT_MASK; vconfig[*curr_idx].is_slave = 1; (*curr_idx)++; //strcpy(vconfig[*curr_idx].ifname,RTL_DRV_PPP2_NETIF_NAME); vconfig[*curr_idx].isWan = 1; vconfig[*curr_idx].if_type = IF_PPPOE; vconfig[*curr_idx].vid = RTL_WANVLANID; vconfig[*curr_idx].fid = RTL_WAN_FID; vconfig[*curr_idx].memPort = RTL_WANPORT_MASK; vconfig[*curr_idx].untagSet = RTL_WANPORT_MASK; vconfig[*curr_idx].is_slave = 1; (*curr_idx)++; //strcpy(vconfig[*curr_idx].ifname,RTL_DRV_PPP3_NETIF_NAME); vconfig[*curr_idx].isWan = 1; vconfig[*curr_idx].if_type = IF_PPPOE; vconfig[*curr_idx].vid = RTL_WANVLANID; vconfig[*curr_idx].fid = RTL_WAN_FID; vconfig[*curr_idx].memPort = RTL_WANPORT_MASK; vconfig[*curr_idx].untagSet = RTL_WANPORT_MASK; vconfig[*curr_idx].is_slave = 1; (*curr_idx)++; //strcpy(vconfig[*curr_idx].ifname,RTL_DRV_PPP4_NETIF_NAME); vconfig[*curr_idx].isWan = 1; vconfig[*curr_idx].if_type = IF_PPPOE; vconfig[*curr_idx].vid = RTL_WANVLANID; vconfig[*curr_idx].fid = RTL_WAN_FID; vconfig[*curr_idx].memPort = RTL_WANPORT_MASK; vconfig[*curr_idx].untagSet = RTL_WANPORT_MASK; vconfig[*curr_idx].is_slave = 1; (*curr_idx)++; //strcpy(vconfig[*curr_idx].ifname,RTL_DRV_PPP5_NETIF_NAME); vconfig[*curr_idx].isWan = 1; vconfig[*curr_idx].if_type = IF_PPPOE; vconfig[*curr_idx].vid = RTL_WANVLANID; vconfig[*curr_idx].fid = RTL_WAN_FID; vconfig[*curr_idx].memPort = RTL_WANPORT_MASK; vconfig[*curr_idx].untagSet = RTL_WANPORT_MASK; vconfig[*curr_idx].is_slave = 1; (*curr_idx)++; //strcpy(vconfig[*curr_idx].ifname,RTL_DRV_PPP6_NETIF_NAME); vconfig[*curr_idx].isWan = 1; vconfig[*curr_idx].if_type = IF_PPPOE; vconfig[*curr_idx].vid = RTL_WANVLANID; vconfig[*curr_idx].fid = RTL_WAN_FID; vconfig[*curr_idx].memPort = RTL_WANPORT_MASK; vconfig[*curr_idx].untagSet = RTL_WANPORT_MASK; vconfig[*curr_idx].is_slave = 1; (*curr_idx)++; #endif //for (i=0; i<RTL8651_PORT_NUMBER+3; i++) // pvid_per_port[i]=RTL_LANVLANID; read_lock(&smux_lock); list_for_each(lh, &grp->virtual_devs) { sdev = list_entry_smuxdev(lh); if (sdev->vid != -1) { idx = getVlanconfigIdx(vconfig, sdev->vid); if (idx <0) break; vconfig[idx].vid = sdev->vid; #ifdef UNIQUE_MAC_PER_DEV if (sdev->vid > 8) #endif //vconfig[idx].untagSet = 0; } else { if (sdev->proto == SMUX_PROTO_BRIDGE) idx = getVlanconfigIdx(vconfig, RTL_BridgeWANVLANID); else idx = getVlanconfigIdx(vconfig, RTL_WANVLANID); if (idx <0) break; if (sdev->proto == SMUX_PROTO_BRIDGE) vconfig[idx].vid = RTL_BridgeWANVLANID; else vconfig[idx].vid = RTL_WANVLANID; } //skip interface name set if this vconfig[idx] had been set to a route intf. if((vconfig[idx].memPort!=RTL_WANPORT_MASK) || (vconfig[idx].untagSet!=0) || (sdev->proto != SMUX_PROTO_BRIDGE)){ strcpy(vconfig[idx].ifname, sdev->vdev->name); memcpy(vconfig[idx].mac.octet, sdev->vdev->dev_addr, ETH_ALEN); } if (sdev->proto == SMUX_PROTO_BRIDGE) { //vconfig[idx].fid = RTL_WAN_FID; //set membership if (sdev->member == 0xFFFFFFFF) { vconfig[idx].memPort |= (RTL_LANPORT_MASK | RTL_WANPORT_MASK); vconfig[idx].untagSet |= (RTL_LANPORT_MASK | RTL_WANPORT_MASK); } else { vconfig[idx].memPort |= ((sdev->member&RTL_LANPORT_MASK)|0x100); vconfig[idx].untagSet |= ((sdev->member&RTL_LANPORT_MASK)|0x100); //for (i=1; i<RTL8651_PORT_NUMBER; i++) { // if (vconfig[idx].memPort & (1<<i)) // pvid_per_port[i] = vconfig[idx].vid; //} } } else{ //set napt vconfig[idx].isWan = sdev->napt; } if ((sdev->vid != -1) #ifdef UNIQUE_MAC_PER_DEV && (sdev->vid > 8) #endif ) { vconfig[idx].untagSet &= (~RTL_WANPORT_MASK); } } read_unlock(&smux_lock); } #endif int smuxUpstreamPortmappingCheck(struct sk_buff *skb) { struct net_device *from, *to; struct smux_dev_info *dev_info,*dev_info2; unsigned int member, port; from = skb->from_dev; to = skb->dev; dev_info = SMUX_DEV_INFO(to); dev_info2 = SMUX_DEV_INFO(from); member = dev_info->member; if (from->priv_flags & IFF_DOMAIN_ELAN) { //sscanf(from->name, "eth0.%d", &port); TOKEN_NUM(from->name,&port); port -= 1; } else if (from->priv_flags & IFF_DOMAIN_WLAN) { TOKEN_NUM(from->name,&port); // sscanf(from->name, "wlan0-vap%d", &port); } else { //printk("%s not from lan dev.\n", __func__); return 0; } if (member & (1<<port)) { return 1; } else { //printk("%s skb block in %s\n", __func__, to->name); return 0; } } int smuxDownstreamPortmappingCheck(struct sk_buff *skb) { struct net_device *from, *to; struct smux_dev_info *dev_info,*dev_info2; unsigned int member, port; from = skb->from_dev; to = skb->dev; dev_info = SMUX_DEV_INFO(from); dev_info2 = SMUX_DEV_INFO(to); //member=skb->vlan_member; member = dev_info->member; if (to->priv_flags & IFF_DOMAIN_ELAN) { //sscanf(to->name, "eth0.%d", &port); TOKEN_NUM(to->name,&port); port -= 1; } else if (to->priv_flags & IFF_DOMAIN_WLAN) { TOKEN_NUM(to->name,&port); // sscanf(to->name, "wlan0-vap%d", &port); } else { // printk("%s not from lan dev.\n", __func__); return 0; } if (member & (1<<port)) { //printk("%s %d from %s to %s member0x%x member & (1<<port)=true \n", __func__, __LINE__, from->name, to->name, member); return 1; } //printk("%s %d from %s to %s member0x%x\n", __func__, __LINE__, from->name, to->name, member); return 0; } #if 0 int smuxDevMacUpdate(unsigned char *pmac) { struct smux_group *grp; struct list_head *lh; struct smux_dev_info * sdev = NULL; //int i; grp = __find_smux_group("nas0"); if (!grp) return; read_lock(&smux_lock); list_for_each(lh, &grp->virtual_devs) { sdev = list_entry_smuxdev(lh); if (sdev->proto != SMUX_PROTO_BRIDGE) { smux_dev_set_mac_address(sdev->vdev, pmac); } } read_unlock(&smux_lock); } #endif #ifdef CONFIG_PORT_MIRROR #define QOS_1P_ENABLE (1<<8) static inline struct sk_buff *smux_mirror_tx_vlan(struct sk_buff *skb, const struct smux_dev_info *dev_info, unsigned int vid, unsigned int priority) { struct sk_buff *skb2, *tmpskb; unsigned char insert[16]; u16 *vlan_tci; //struct ethhdr *eth; struct vlan_ethhdr *veth; skb2 = skb_copy(skb, GFP_ATOMIC); if (skb_headroom(skb2) < 4) { tmpskb = skb_realloc_headroom(skb2, 4); dev_kfree_skb_any(skb2); if (tmpskb == NULL) return 0; skb2 = tmpskb; } skb2->dev = dev_info->mirror_dev; if (skb2->mark&QOS_1P_ENABLE) { // IP_QoS 802.1p remarking insert[0] = ((skb2->mark&0x7)<<5) | (vid>>8); } else insert[0] = (priority<<5) | (vid>>8); insert[1] = vid&0xff; vlan_tci = (u16 *)insert; veth = (struct vlan_ethhdr *)skb_push(skb2, VLAN_HLEN); /* Move the mac addresses to the beginning of the new header. */ memmove(skb2->data, skb2->data + VLAN_HLEN, 2 * VLAN_ETH_ALEN); /* first, the ethernet type */ veth->h_vlan_proto = htons(ETH_P_8021Q); /* now, the TCI */ veth->h_vlan_TCI = htons(*vlan_tci); return skb2; } static inline void smux_mirror_pkt(struct sk_buff *skb, const struct smux_dev_info *dev_info, const int flag) { struct sk_buff *skb2=0; //AUG_DBG("the dev_info->port_mirror is %d\n", dev_info->port_mirror); if ((OUT == flag) && (skb->vlan_tci&VLAN_TAG_PRESENT)) { // insert vlan tag skb2 = smux_mirror_tx_vlan(skb, dev_info, skb->vlan_tci&VLAN_VID_MASK, (skb->vlan_tci>>VLAN_PRIO_SHIFT)&0x07); } else if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) { skb2->dev = dev_info->mirror_dev; if(IN == flag) skb_push(skb2, ETH_HLEN); } //AUG_DBG("the dev_info->mirror_dev is %s\n", dev_info->mirror_dev->name); if (skb2) nic_tx_mirror(skb2); } #endif module_init(smux_drv_init); module_exit(smux_drv_exit); EXPORT_SYMBOL(smux_pkt_recv);