162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Ethernet netdevice using ATM AAL5 as underlying carrier 462306a36Sopenharmony_ci * (RFC1483 obsoleted by RFC2684) for Linux 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Authors: Marcell GAL, 2000, XDSL Ltd, Hungary 762306a36Sopenharmony_ci * Eric Kinzie, 2006-2007, US Naval Research Laboratory 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/list.h> 1662306a36Sopenharmony_ci#include <linux/netdevice.h> 1762306a36Sopenharmony_ci#include <linux/skbuff.h> 1862306a36Sopenharmony_ci#include <linux/etherdevice.h> 1962306a36Sopenharmony_ci#include <linux/rtnetlink.h> 2062306a36Sopenharmony_ci#include <linux/ip.h> 2162306a36Sopenharmony_ci#include <linux/uaccess.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <net/arp.h> 2462306a36Sopenharmony_ci#include <linux/atm.h> 2562306a36Sopenharmony_ci#include <linux/atmdev.h> 2662306a36Sopenharmony_ci#include <linux/capability.h> 2762306a36Sopenharmony_ci#include <linux/seq_file.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <linux/atmbr2684.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include "common.h" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic void skb_debug(const struct sk_buff *skb) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci#ifdef SKB_DEBUG 3662306a36Sopenharmony_ci#define NUM2PRINT 50 3762306a36Sopenharmony_ci print_hex_dump(KERN_DEBUG, "br2684: skb: ", DUMP_OFFSET, 3862306a36Sopenharmony_ci 16, 1, skb->data, min(NUM2PRINT, skb->len), true); 3962306a36Sopenharmony_ci#endif 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define BR2684_ETHERTYPE_LEN 2 4362306a36Sopenharmony_ci#define BR2684_PAD_LEN 2 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define LLC 0xaa, 0xaa, 0x03 4662306a36Sopenharmony_ci#define SNAP_BRIDGED 0x00, 0x80, 0xc2 4762306a36Sopenharmony_ci#define SNAP_ROUTED 0x00, 0x00, 0x00 4862306a36Sopenharmony_ci#define PID_ETHERNET 0x00, 0x07 4962306a36Sopenharmony_ci#define ETHERTYPE_IPV4 0x08, 0x00 5062306a36Sopenharmony_ci#define ETHERTYPE_IPV6 0x86, 0xdd 5162306a36Sopenharmony_ci#define PAD_BRIDGED 0x00, 0x00 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic const unsigned char ethertype_ipv4[] = { ETHERTYPE_IPV4 }; 5462306a36Sopenharmony_cistatic const unsigned char ethertype_ipv6[] = { ETHERTYPE_IPV6 }; 5562306a36Sopenharmony_cistatic const unsigned char llc_oui_pid_pad[] = 5662306a36Sopenharmony_ci { LLC, SNAP_BRIDGED, PID_ETHERNET, PAD_BRIDGED }; 5762306a36Sopenharmony_cistatic const unsigned char pad[] = { PAD_BRIDGED }; 5862306a36Sopenharmony_cistatic const unsigned char llc_oui_ipv4[] = { LLC, SNAP_ROUTED, ETHERTYPE_IPV4 }; 5962306a36Sopenharmony_cistatic const unsigned char llc_oui_ipv6[] = { LLC, SNAP_ROUTED, ETHERTYPE_IPV6 }; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cienum br2684_encaps { 6262306a36Sopenharmony_ci e_vc = BR2684_ENCAPS_VC, 6362306a36Sopenharmony_ci e_llc = BR2684_ENCAPS_LLC, 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistruct br2684_vcc { 6762306a36Sopenharmony_ci struct atm_vcc *atmvcc; 6862306a36Sopenharmony_ci struct net_device *device; 6962306a36Sopenharmony_ci /* keep old push, pop functions for chaining */ 7062306a36Sopenharmony_ci void (*old_push)(struct atm_vcc *vcc, struct sk_buff *skb); 7162306a36Sopenharmony_ci void (*old_pop)(struct atm_vcc *vcc, struct sk_buff *skb); 7262306a36Sopenharmony_ci void (*old_release_cb)(struct atm_vcc *vcc); 7362306a36Sopenharmony_ci struct module *old_owner; 7462306a36Sopenharmony_ci enum br2684_encaps encaps; 7562306a36Sopenharmony_ci struct list_head brvccs; 7662306a36Sopenharmony_ci#ifdef CONFIG_ATM_BR2684_IPFILTER 7762306a36Sopenharmony_ci struct br2684_filter filter; 7862306a36Sopenharmony_ci#endif /* CONFIG_ATM_BR2684_IPFILTER */ 7962306a36Sopenharmony_ci unsigned int copies_needed, copies_failed; 8062306a36Sopenharmony_ci atomic_t qspace; 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistruct br2684_dev { 8462306a36Sopenharmony_ci struct net_device *net_dev; 8562306a36Sopenharmony_ci struct list_head br2684_devs; 8662306a36Sopenharmony_ci int number; 8762306a36Sopenharmony_ci struct list_head brvccs; /* one device <=> one vcc (before xmas) */ 8862306a36Sopenharmony_ci int mac_was_set; 8962306a36Sopenharmony_ci enum br2684_payload payload; 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* 9362306a36Sopenharmony_ci * This lock should be held for writing any time the list of devices or 9462306a36Sopenharmony_ci * their attached vcc's could be altered. It should be held for reading 9562306a36Sopenharmony_ci * any time these are being queried. Note that we sometimes need to 9662306a36Sopenharmony_ci * do read-locking under interrupting context, so write locking must block 9762306a36Sopenharmony_ci * the current CPU's interrupts. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_cistatic DEFINE_RWLOCK(devs_lock); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic LIST_HEAD(br2684_devs); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic inline struct br2684_dev *BRPRIV(const struct net_device *net_dev) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci return netdev_priv(net_dev); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic inline struct net_device *list_entry_brdev(const struct list_head *le) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci return list_entry(le, struct br2684_dev, br2684_devs)->net_dev; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic inline struct br2684_vcc *BR2684_VCC(const struct atm_vcc *atmvcc) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci return (struct br2684_vcc *)(atmvcc->user_back); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic inline struct br2684_vcc *list_entry_brvcc(const struct list_head *le) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci return list_entry(le, struct br2684_vcc, brvccs); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* Caller should hold read_lock(&devs_lock) */ 12462306a36Sopenharmony_cistatic struct net_device *br2684_find_dev(const struct br2684_if_spec *s) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct list_head *lh; 12762306a36Sopenharmony_ci struct net_device *net_dev; 12862306a36Sopenharmony_ci switch (s->method) { 12962306a36Sopenharmony_ci case BR2684_FIND_BYNUM: 13062306a36Sopenharmony_ci list_for_each(lh, &br2684_devs) { 13162306a36Sopenharmony_ci net_dev = list_entry_brdev(lh); 13262306a36Sopenharmony_ci if (BRPRIV(net_dev)->number == s->spec.devnum) 13362306a36Sopenharmony_ci return net_dev; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci break; 13662306a36Sopenharmony_ci case BR2684_FIND_BYIFNAME: 13762306a36Sopenharmony_ci list_for_each(lh, &br2684_devs) { 13862306a36Sopenharmony_ci net_dev = list_entry_brdev(lh); 13962306a36Sopenharmony_ci if (!strncmp(net_dev->name, s->spec.ifname, IFNAMSIZ)) 14062306a36Sopenharmony_ci return net_dev; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci break; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci return NULL; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic int atm_dev_event(struct notifier_block *this, unsigned long event, 14862306a36Sopenharmony_ci void *arg) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct atm_dev *atm_dev = arg; 15162306a36Sopenharmony_ci struct list_head *lh; 15262306a36Sopenharmony_ci struct net_device *net_dev; 15362306a36Sopenharmony_ci struct br2684_vcc *brvcc; 15462306a36Sopenharmony_ci struct atm_vcc *atm_vcc; 15562306a36Sopenharmony_ci unsigned long flags; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci pr_debug("event=%ld dev=%p\n", event, atm_dev); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci read_lock_irqsave(&devs_lock, flags); 16062306a36Sopenharmony_ci list_for_each(lh, &br2684_devs) { 16162306a36Sopenharmony_ci net_dev = list_entry_brdev(lh); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci list_for_each_entry(brvcc, &BRPRIV(net_dev)->brvccs, brvccs) { 16462306a36Sopenharmony_ci atm_vcc = brvcc->atmvcc; 16562306a36Sopenharmony_ci if (atm_vcc && brvcc->atmvcc->dev == atm_dev) { 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (atm_vcc->dev->signal == ATM_PHY_SIG_LOST) 16862306a36Sopenharmony_ci netif_carrier_off(net_dev); 16962306a36Sopenharmony_ci else 17062306a36Sopenharmony_ci netif_carrier_on(net_dev); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci read_unlock_irqrestore(&devs_lock, flags); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci return NOTIFY_DONE; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic struct notifier_block atm_dev_notifier = { 18162306a36Sopenharmony_ci .notifier_call = atm_dev_event, 18262306a36Sopenharmony_ci}; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci/* chained vcc->pop function. Check if we should wake the netif_queue */ 18562306a36Sopenharmony_cistatic void br2684_pop(struct atm_vcc *vcc, struct sk_buff *skb) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct br2684_vcc *brvcc = BR2684_VCC(vcc); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci pr_debug("(vcc %p ; net_dev %p )\n", vcc, brvcc->device); 19062306a36Sopenharmony_ci brvcc->old_pop(vcc, skb); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* If the queue space just went up from zero, wake */ 19362306a36Sopenharmony_ci if (atomic_inc_return(&brvcc->qspace) == 1) 19462306a36Sopenharmony_ci netif_wake_queue(brvcc->device); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci/* 19862306a36Sopenharmony_ci * Send a packet out a particular vcc. Not to useful right now, but paves 19962306a36Sopenharmony_ci * the way for multiple vcc's per itf. Returns true if we can send, 20062306a36Sopenharmony_ci * otherwise false 20162306a36Sopenharmony_ci */ 20262306a36Sopenharmony_cistatic int br2684_xmit_vcc(struct sk_buff *skb, struct net_device *dev, 20362306a36Sopenharmony_ci struct br2684_vcc *brvcc) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct br2684_dev *brdev = BRPRIV(dev); 20662306a36Sopenharmony_ci struct atm_vcc *atmvcc; 20762306a36Sopenharmony_ci int minheadroom = (brvcc->encaps == e_llc) ? 20862306a36Sopenharmony_ci ((brdev->payload == p_bridged) ? 20962306a36Sopenharmony_ci sizeof(llc_oui_pid_pad) : sizeof(llc_oui_ipv4)) : 21062306a36Sopenharmony_ci ((brdev->payload == p_bridged) ? BR2684_PAD_LEN : 0); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (skb_headroom(skb) < minheadroom) { 21362306a36Sopenharmony_ci struct sk_buff *skb2 = skb_realloc_headroom(skb, minheadroom); 21462306a36Sopenharmony_ci brvcc->copies_needed++; 21562306a36Sopenharmony_ci dev_kfree_skb(skb); 21662306a36Sopenharmony_ci if (skb2 == NULL) { 21762306a36Sopenharmony_ci brvcc->copies_failed++; 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci skb = skb2; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (brvcc->encaps == e_llc) { 22462306a36Sopenharmony_ci if (brdev->payload == p_bridged) { 22562306a36Sopenharmony_ci skb_push(skb, sizeof(llc_oui_pid_pad)); 22662306a36Sopenharmony_ci skb_copy_to_linear_data(skb, llc_oui_pid_pad, 22762306a36Sopenharmony_ci sizeof(llc_oui_pid_pad)); 22862306a36Sopenharmony_ci } else if (brdev->payload == p_routed) { 22962306a36Sopenharmony_ci unsigned short prot = ntohs(skb->protocol); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci skb_push(skb, sizeof(llc_oui_ipv4)); 23262306a36Sopenharmony_ci switch (prot) { 23362306a36Sopenharmony_ci case ETH_P_IP: 23462306a36Sopenharmony_ci skb_copy_to_linear_data(skb, llc_oui_ipv4, 23562306a36Sopenharmony_ci sizeof(llc_oui_ipv4)); 23662306a36Sopenharmony_ci break; 23762306a36Sopenharmony_ci case ETH_P_IPV6: 23862306a36Sopenharmony_ci skb_copy_to_linear_data(skb, llc_oui_ipv6, 23962306a36Sopenharmony_ci sizeof(llc_oui_ipv6)); 24062306a36Sopenharmony_ci break; 24162306a36Sopenharmony_ci default: 24262306a36Sopenharmony_ci dev_kfree_skb(skb); 24362306a36Sopenharmony_ci return 0; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci } else { /* e_vc */ 24762306a36Sopenharmony_ci if (brdev->payload == p_bridged) { 24862306a36Sopenharmony_ci skb_push(skb, 2); 24962306a36Sopenharmony_ci memset(skb->data, 0, 2); 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci skb_debug(skb); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci ATM_SKB(skb)->vcc = atmvcc = brvcc->atmvcc; 25562306a36Sopenharmony_ci pr_debug("atm_skb(%p)->vcc(%p)->dev(%p)\n", skb, atmvcc, atmvcc->dev); 25662306a36Sopenharmony_ci atm_account_tx(atmvcc, skb); 25762306a36Sopenharmony_ci dev->stats.tx_packets++; 25862306a36Sopenharmony_ci dev->stats.tx_bytes += skb->len; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (atomic_dec_return(&brvcc->qspace) < 1) { 26162306a36Sopenharmony_ci /* No more please! */ 26262306a36Sopenharmony_ci netif_stop_queue(brvcc->device); 26362306a36Sopenharmony_ci /* We might have raced with br2684_pop() */ 26462306a36Sopenharmony_ci if (unlikely(atomic_read(&brvcc->qspace) > 0)) 26562306a36Sopenharmony_ci netif_wake_queue(brvcc->device); 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* If this fails immediately, the skb will be freed and br2684_pop() 26962306a36Sopenharmony_ci will wake the queue if appropriate. Just return an error so that 27062306a36Sopenharmony_ci the stats are updated correctly */ 27162306a36Sopenharmony_ci return !atmvcc->send(atmvcc, skb); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic void br2684_release_cb(struct atm_vcc *atmvcc) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci struct br2684_vcc *brvcc = BR2684_VCC(atmvcc); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (atomic_read(&brvcc->qspace) > 0) 27962306a36Sopenharmony_ci netif_wake_queue(brvcc->device); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (brvcc->old_release_cb) 28262306a36Sopenharmony_ci brvcc->old_release_cb(atmvcc); 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic inline struct br2684_vcc *pick_outgoing_vcc(const struct sk_buff *skb, 28662306a36Sopenharmony_ci const struct br2684_dev *brdev) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci return list_empty(&brdev->brvccs) ? NULL : list_entry_brvcc(brdev->brvccs.next); /* 1 vcc/dev right now */ 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic netdev_tx_t br2684_start_xmit(struct sk_buff *skb, 29262306a36Sopenharmony_ci struct net_device *dev) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci struct br2684_dev *brdev = BRPRIV(dev); 29562306a36Sopenharmony_ci struct br2684_vcc *brvcc; 29662306a36Sopenharmony_ci struct atm_vcc *atmvcc; 29762306a36Sopenharmony_ci netdev_tx_t ret = NETDEV_TX_OK; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci pr_debug("skb_dst(skb)=%p\n", skb_dst(skb)); 30062306a36Sopenharmony_ci read_lock(&devs_lock); 30162306a36Sopenharmony_ci brvcc = pick_outgoing_vcc(skb, brdev); 30262306a36Sopenharmony_ci if (brvcc == NULL) { 30362306a36Sopenharmony_ci pr_debug("no vcc attached to dev %s\n", dev->name); 30462306a36Sopenharmony_ci dev->stats.tx_errors++; 30562306a36Sopenharmony_ci dev->stats.tx_carrier_errors++; 30662306a36Sopenharmony_ci /* netif_stop_queue(dev); */ 30762306a36Sopenharmony_ci dev_kfree_skb(skb); 30862306a36Sopenharmony_ci goto out_devs; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci atmvcc = brvcc->atmvcc; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci bh_lock_sock(sk_atm(atmvcc)); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (test_bit(ATM_VF_RELEASED, &atmvcc->flags) || 31562306a36Sopenharmony_ci test_bit(ATM_VF_CLOSE, &atmvcc->flags) || 31662306a36Sopenharmony_ci !test_bit(ATM_VF_READY, &atmvcc->flags)) { 31762306a36Sopenharmony_ci dev->stats.tx_dropped++; 31862306a36Sopenharmony_ci dev_kfree_skb(skb); 31962306a36Sopenharmony_ci goto out; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (sock_owned_by_user(sk_atm(atmvcc))) { 32362306a36Sopenharmony_ci netif_stop_queue(brvcc->device); 32462306a36Sopenharmony_ci ret = NETDEV_TX_BUSY; 32562306a36Sopenharmony_ci goto out; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (!br2684_xmit_vcc(skb, dev, brvcc)) { 32962306a36Sopenharmony_ci /* 33062306a36Sopenharmony_ci * We should probably use netif_*_queue() here, but that 33162306a36Sopenharmony_ci * involves added complication. We need to walk before 33262306a36Sopenharmony_ci * we can run. 33362306a36Sopenharmony_ci * 33462306a36Sopenharmony_ci * Don't free here! this pointer might be no longer valid! 33562306a36Sopenharmony_ci */ 33662306a36Sopenharmony_ci dev->stats.tx_errors++; 33762306a36Sopenharmony_ci dev->stats.tx_fifo_errors++; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci out: 34062306a36Sopenharmony_ci bh_unlock_sock(sk_atm(atmvcc)); 34162306a36Sopenharmony_ci out_devs: 34262306a36Sopenharmony_ci read_unlock(&devs_lock); 34362306a36Sopenharmony_ci return ret; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci/* 34762306a36Sopenharmony_ci * We remember when the MAC gets set, so we don't override it later with 34862306a36Sopenharmony_ci * the ESI of the ATM card of the first VC 34962306a36Sopenharmony_ci */ 35062306a36Sopenharmony_cistatic int br2684_mac_addr(struct net_device *dev, void *p) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci int err = eth_mac_addr(dev, p); 35362306a36Sopenharmony_ci if (!err) 35462306a36Sopenharmony_ci BRPRIV(dev)->mac_was_set = 1; 35562306a36Sopenharmony_ci return err; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci#ifdef CONFIG_ATM_BR2684_IPFILTER 35962306a36Sopenharmony_ci/* this IOCTL is experimental. */ 36062306a36Sopenharmony_cistatic int br2684_setfilt(struct atm_vcc *atmvcc, void __user * arg) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci struct br2684_vcc *brvcc; 36362306a36Sopenharmony_ci struct br2684_filter_set fs; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (copy_from_user(&fs, arg, sizeof fs)) 36662306a36Sopenharmony_ci return -EFAULT; 36762306a36Sopenharmony_ci if (fs.ifspec.method != BR2684_FIND_BYNOTHING) { 36862306a36Sopenharmony_ci /* 36962306a36Sopenharmony_ci * This is really a per-vcc thing, but we can also search 37062306a36Sopenharmony_ci * by device. 37162306a36Sopenharmony_ci */ 37262306a36Sopenharmony_ci struct br2684_dev *brdev; 37362306a36Sopenharmony_ci read_lock(&devs_lock); 37462306a36Sopenharmony_ci brdev = BRPRIV(br2684_find_dev(&fs.ifspec)); 37562306a36Sopenharmony_ci if (brdev == NULL || list_empty(&brdev->brvccs) || 37662306a36Sopenharmony_ci brdev->brvccs.next != brdev->brvccs.prev) /* >1 VCC */ 37762306a36Sopenharmony_ci brvcc = NULL; 37862306a36Sopenharmony_ci else 37962306a36Sopenharmony_ci brvcc = list_entry_brvcc(brdev->brvccs.next); 38062306a36Sopenharmony_ci read_unlock(&devs_lock); 38162306a36Sopenharmony_ci if (brvcc == NULL) 38262306a36Sopenharmony_ci return -ESRCH; 38362306a36Sopenharmony_ci } else 38462306a36Sopenharmony_ci brvcc = BR2684_VCC(atmvcc); 38562306a36Sopenharmony_ci memcpy(&brvcc->filter, &fs.filter, sizeof(brvcc->filter)); 38662306a36Sopenharmony_ci return 0; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci/* Returns 1 if packet should be dropped */ 39062306a36Sopenharmony_cistatic inline int 39162306a36Sopenharmony_cipacket_fails_filter(__be16 type, struct br2684_vcc *brvcc, struct sk_buff *skb) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci if (brvcc->filter.netmask == 0) 39462306a36Sopenharmony_ci return 0; /* no filter in place */ 39562306a36Sopenharmony_ci if (type == htons(ETH_P_IP) && 39662306a36Sopenharmony_ci (((struct iphdr *)(skb->data))->daddr & brvcc->filter. 39762306a36Sopenharmony_ci netmask) == brvcc->filter.prefix) 39862306a36Sopenharmony_ci return 0; 39962306a36Sopenharmony_ci if (type == htons(ETH_P_ARP)) 40062306a36Sopenharmony_ci return 0; 40162306a36Sopenharmony_ci /* 40262306a36Sopenharmony_ci * TODO: we should probably filter ARPs too.. don't want to have 40362306a36Sopenharmony_ci * them returning values that don't make sense, or is that ok? 40462306a36Sopenharmony_ci */ 40562306a36Sopenharmony_ci return 1; /* drop */ 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci#endif /* CONFIG_ATM_BR2684_IPFILTER */ 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic void br2684_close_vcc(struct br2684_vcc *brvcc) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci pr_debug("removing VCC %p from dev %p\n", brvcc, brvcc->device); 41262306a36Sopenharmony_ci write_lock_irq(&devs_lock); 41362306a36Sopenharmony_ci list_del(&brvcc->brvccs); 41462306a36Sopenharmony_ci write_unlock_irq(&devs_lock); 41562306a36Sopenharmony_ci brvcc->atmvcc->user_back = NULL; /* what about vcc->recvq ??? */ 41662306a36Sopenharmony_ci brvcc->atmvcc->release_cb = brvcc->old_release_cb; 41762306a36Sopenharmony_ci brvcc->old_push(brvcc->atmvcc, NULL); /* pass on the bad news */ 41862306a36Sopenharmony_ci module_put(brvcc->old_owner); 41962306a36Sopenharmony_ci kfree(brvcc); 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci/* when AAL5 PDU comes in: */ 42362306a36Sopenharmony_cistatic void br2684_push(struct atm_vcc *atmvcc, struct sk_buff *skb) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci struct br2684_vcc *brvcc = BR2684_VCC(atmvcc); 42662306a36Sopenharmony_ci struct net_device *net_dev = brvcc->device; 42762306a36Sopenharmony_ci struct br2684_dev *brdev = BRPRIV(net_dev); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci pr_debug("\n"); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if (unlikely(skb == NULL)) { 43262306a36Sopenharmony_ci /* skb==NULL means VCC is being destroyed */ 43362306a36Sopenharmony_ci br2684_close_vcc(brvcc); 43462306a36Sopenharmony_ci if (list_empty(&brdev->brvccs)) { 43562306a36Sopenharmony_ci write_lock_irq(&devs_lock); 43662306a36Sopenharmony_ci list_del(&brdev->br2684_devs); 43762306a36Sopenharmony_ci write_unlock_irq(&devs_lock); 43862306a36Sopenharmony_ci unregister_netdev(net_dev); 43962306a36Sopenharmony_ci free_netdev(net_dev); 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci return; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci skb_debug(skb); 44562306a36Sopenharmony_ci atm_return(atmvcc, skb->truesize); 44662306a36Sopenharmony_ci pr_debug("skb from brdev %p\n", brdev); 44762306a36Sopenharmony_ci if (brvcc->encaps == e_llc) { 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (skb->len > 7 && skb->data[7] == 0x01) 45062306a36Sopenharmony_ci __skb_trim(skb, skb->len - 4); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /* accept packets that have "ipv[46]" in the snap header */ 45362306a36Sopenharmony_ci if ((skb->len >= (sizeof(llc_oui_ipv4))) && 45462306a36Sopenharmony_ci (memcmp(skb->data, llc_oui_ipv4, 45562306a36Sopenharmony_ci sizeof(llc_oui_ipv4) - BR2684_ETHERTYPE_LEN) == 0)) { 45662306a36Sopenharmony_ci if (memcmp(skb->data + 6, ethertype_ipv6, 45762306a36Sopenharmony_ci sizeof(ethertype_ipv6)) == 0) 45862306a36Sopenharmony_ci skb->protocol = htons(ETH_P_IPV6); 45962306a36Sopenharmony_ci else if (memcmp(skb->data + 6, ethertype_ipv4, 46062306a36Sopenharmony_ci sizeof(ethertype_ipv4)) == 0) 46162306a36Sopenharmony_ci skb->protocol = htons(ETH_P_IP); 46262306a36Sopenharmony_ci else 46362306a36Sopenharmony_ci goto error; 46462306a36Sopenharmony_ci skb_pull(skb, sizeof(llc_oui_ipv4)); 46562306a36Sopenharmony_ci skb_reset_network_header(skb); 46662306a36Sopenharmony_ci skb->pkt_type = PACKET_HOST; 46762306a36Sopenharmony_ci /* 46862306a36Sopenharmony_ci * Let us waste some time for checking the encapsulation. 46962306a36Sopenharmony_ci * Note, that only 7 char is checked so frames with a valid FCS 47062306a36Sopenharmony_ci * are also accepted (but FCS is not checked of course). 47162306a36Sopenharmony_ci */ 47262306a36Sopenharmony_ci } else if ((skb->len >= sizeof(llc_oui_pid_pad)) && 47362306a36Sopenharmony_ci (memcmp(skb->data, llc_oui_pid_pad, 7) == 0)) { 47462306a36Sopenharmony_ci skb_pull(skb, sizeof(llc_oui_pid_pad)); 47562306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, net_dev); 47662306a36Sopenharmony_ci } else 47762306a36Sopenharmony_ci goto error; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci } else { /* e_vc */ 48062306a36Sopenharmony_ci if (brdev->payload == p_routed) { 48162306a36Sopenharmony_ci struct iphdr *iph; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci skb_reset_network_header(skb); 48462306a36Sopenharmony_ci iph = ip_hdr(skb); 48562306a36Sopenharmony_ci if (iph->version == 4) 48662306a36Sopenharmony_ci skb->protocol = htons(ETH_P_IP); 48762306a36Sopenharmony_ci else if (iph->version == 6) 48862306a36Sopenharmony_ci skb->protocol = htons(ETH_P_IPV6); 48962306a36Sopenharmony_ci else 49062306a36Sopenharmony_ci goto error; 49162306a36Sopenharmony_ci skb->pkt_type = PACKET_HOST; 49262306a36Sopenharmony_ci } else { /* p_bridged */ 49362306a36Sopenharmony_ci /* first 2 chars should be 0 */ 49462306a36Sopenharmony_ci if (memcmp(skb->data, pad, BR2684_PAD_LEN) != 0) 49562306a36Sopenharmony_ci goto error; 49662306a36Sopenharmony_ci skb_pull(skb, BR2684_PAD_LEN); 49762306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, net_dev); 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci#ifdef CONFIG_ATM_BR2684_IPFILTER 50262306a36Sopenharmony_ci if (unlikely(packet_fails_filter(skb->protocol, brvcc, skb))) 50362306a36Sopenharmony_ci goto dropped; 50462306a36Sopenharmony_ci#endif /* CONFIG_ATM_BR2684_IPFILTER */ 50562306a36Sopenharmony_ci skb->dev = net_dev; 50662306a36Sopenharmony_ci ATM_SKB(skb)->vcc = atmvcc; /* needed ? */ 50762306a36Sopenharmony_ci pr_debug("received packet's protocol: %x\n", ntohs(skb->protocol)); 50862306a36Sopenharmony_ci skb_debug(skb); 50962306a36Sopenharmony_ci /* sigh, interface is down? */ 51062306a36Sopenharmony_ci if (unlikely(!(net_dev->flags & IFF_UP))) 51162306a36Sopenharmony_ci goto dropped; 51262306a36Sopenharmony_ci net_dev->stats.rx_packets++; 51362306a36Sopenharmony_ci net_dev->stats.rx_bytes += skb->len; 51462306a36Sopenharmony_ci memset(ATM_SKB(skb), 0, sizeof(struct atm_skb_data)); 51562306a36Sopenharmony_ci netif_rx(skb); 51662306a36Sopenharmony_ci return; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_cidropped: 51962306a36Sopenharmony_ci net_dev->stats.rx_dropped++; 52062306a36Sopenharmony_ci goto free_skb; 52162306a36Sopenharmony_cierror: 52262306a36Sopenharmony_ci net_dev->stats.rx_errors++; 52362306a36Sopenharmony_cifree_skb: 52462306a36Sopenharmony_ci dev_kfree_skb(skb); 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci/* 52862306a36Sopenharmony_ci * Assign a vcc to a dev 52962306a36Sopenharmony_ci * Note: we do not have explicit unassign, but look at _push() 53062306a36Sopenharmony_ci */ 53162306a36Sopenharmony_cistatic int br2684_regvcc(struct atm_vcc *atmvcc, void __user * arg) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci struct br2684_vcc *brvcc; 53462306a36Sopenharmony_ci struct br2684_dev *brdev; 53562306a36Sopenharmony_ci struct net_device *net_dev; 53662306a36Sopenharmony_ci struct atm_backend_br2684 be; 53762306a36Sopenharmony_ci int err; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (copy_from_user(&be, arg, sizeof be)) 54062306a36Sopenharmony_ci return -EFAULT; 54162306a36Sopenharmony_ci brvcc = kzalloc(sizeof(struct br2684_vcc), GFP_KERNEL); 54262306a36Sopenharmony_ci if (!brvcc) 54362306a36Sopenharmony_ci return -ENOMEM; 54462306a36Sopenharmony_ci /* 54562306a36Sopenharmony_ci * Allow two packets in the ATM queue. One actually being sent, and one 54662306a36Sopenharmony_ci * for the ATM 'TX done' handler to send. It shouldn't take long to get 54762306a36Sopenharmony_ci * the next one from the netdev queue, when we need it. More than that 54862306a36Sopenharmony_ci * would be bufferbloat. 54962306a36Sopenharmony_ci */ 55062306a36Sopenharmony_ci atomic_set(&brvcc->qspace, 2); 55162306a36Sopenharmony_ci write_lock_irq(&devs_lock); 55262306a36Sopenharmony_ci net_dev = br2684_find_dev(&be.ifspec); 55362306a36Sopenharmony_ci if (net_dev == NULL) { 55462306a36Sopenharmony_ci pr_err("tried to attach to non-existent device\n"); 55562306a36Sopenharmony_ci err = -ENXIO; 55662306a36Sopenharmony_ci goto error; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci brdev = BRPRIV(net_dev); 55962306a36Sopenharmony_ci if (atmvcc->push == NULL) { 56062306a36Sopenharmony_ci err = -EBADFD; 56162306a36Sopenharmony_ci goto error; 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci if (!list_empty(&brdev->brvccs)) { 56462306a36Sopenharmony_ci /* Only 1 VCC/dev right now */ 56562306a36Sopenharmony_ci err = -EEXIST; 56662306a36Sopenharmony_ci goto error; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci if (be.fcs_in != BR2684_FCSIN_NO || 56962306a36Sopenharmony_ci be.fcs_out != BR2684_FCSOUT_NO || 57062306a36Sopenharmony_ci be.fcs_auto || be.has_vpiid || be.send_padding || 57162306a36Sopenharmony_ci (be.encaps != BR2684_ENCAPS_VC && 57262306a36Sopenharmony_ci be.encaps != BR2684_ENCAPS_LLC) || 57362306a36Sopenharmony_ci be.min_size != 0) { 57462306a36Sopenharmony_ci err = -EINVAL; 57562306a36Sopenharmony_ci goto error; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci pr_debug("vcc=%p, encaps=%d, brvcc=%p\n", atmvcc, be.encaps, brvcc); 57862306a36Sopenharmony_ci if (list_empty(&brdev->brvccs) && !brdev->mac_was_set) { 57962306a36Sopenharmony_ci unsigned char *esi = atmvcc->dev->esi; 58062306a36Sopenharmony_ci const u8 one = 1; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci if (esi[0] | esi[1] | esi[2] | esi[3] | esi[4] | esi[5]) 58362306a36Sopenharmony_ci dev_addr_set(net_dev, esi); 58462306a36Sopenharmony_ci else 58562306a36Sopenharmony_ci dev_addr_mod(net_dev, 2, &one, 1); 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci list_add(&brvcc->brvccs, &brdev->brvccs); 58862306a36Sopenharmony_ci write_unlock_irq(&devs_lock); 58962306a36Sopenharmony_ci brvcc->device = net_dev; 59062306a36Sopenharmony_ci brvcc->atmvcc = atmvcc; 59162306a36Sopenharmony_ci atmvcc->user_back = brvcc; 59262306a36Sopenharmony_ci brvcc->encaps = (enum br2684_encaps)be.encaps; 59362306a36Sopenharmony_ci brvcc->old_push = atmvcc->push; 59462306a36Sopenharmony_ci brvcc->old_pop = atmvcc->pop; 59562306a36Sopenharmony_ci brvcc->old_release_cb = atmvcc->release_cb; 59662306a36Sopenharmony_ci brvcc->old_owner = atmvcc->owner; 59762306a36Sopenharmony_ci barrier(); 59862306a36Sopenharmony_ci atmvcc->push = br2684_push; 59962306a36Sopenharmony_ci atmvcc->pop = br2684_pop; 60062306a36Sopenharmony_ci atmvcc->release_cb = br2684_release_cb; 60162306a36Sopenharmony_ci atmvcc->owner = THIS_MODULE; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci /* initialize netdev carrier state */ 60462306a36Sopenharmony_ci if (atmvcc->dev->signal == ATM_PHY_SIG_LOST) 60562306a36Sopenharmony_ci netif_carrier_off(net_dev); 60662306a36Sopenharmony_ci else 60762306a36Sopenharmony_ci netif_carrier_on(net_dev); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci __module_get(THIS_MODULE); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci /* re-process everything received between connection setup and 61262306a36Sopenharmony_ci backend setup */ 61362306a36Sopenharmony_ci vcc_process_recv_queue(atmvcc); 61462306a36Sopenharmony_ci return 0; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cierror: 61762306a36Sopenharmony_ci write_unlock_irq(&devs_lock); 61862306a36Sopenharmony_ci kfree(brvcc); 61962306a36Sopenharmony_ci return err; 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cistatic const struct net_device_ops br2684_netdev_ops = { 62362306a36Sopenharmony_ci .ndo_start_xmit = br2684_start_xmit, 62462306a36Sopenharmony_ci .ndo_set_mac_address = br2684_mac_addr, 62562306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 62662306a36Sopenharmony_ci}; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_cistatic const struct net_device_ops br2684_netdev_ops_routed = { 62962306a36Sopenharmony_ci .ndo_start_xmit = br2684_start_xmit, 63062306a36Sopenharmony_ci .ndo_set_mac_address = br2684_mac_addr, 63162306a36Sopenharmony_ci}; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic void br2684_setup(struct net_device *netdev) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci struct br2684_dev *brdev = BRPRIV(netdev); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci ether_setup(netdev); 63862306a36Sopenharmony_ci netdev->hard_header_len += sizeof(llc_oui_pid_pad); /* worst case */ 63962306a36Sopenharmony_ci brdev->net_dev = netdev; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci netdev->netdev_ops = &br2684_netdev_ops; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci INIT_LIST_HEAD(&brdev->brvccs); 64462306a36Sopenharmony_ci} 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_cistatic void br2684_setup_routed(struct net_device *netdev) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci struct br2684_dev *brdev = BRPRIV(netdev); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci brdev->net_dev = netdev; 65162306a36Sopenharmony_ci netdev->hard_header_len = sizeof(llc_oui_ipv4); /* worst case */ 65262306a36Sopenharmony_ci netdev->netdev_ops = &br2684_netdev_ops_routed; 65362306a36Sopenharmony_ci netdev->addr_len = 0; 65462306a36Sopenharmony_ci netdev->mtu = ETH_DATA_LEN; 65562306a36Sopenharmony_ci netdev->min_mtu = 0; 65662306a36Sopenharmony_ci netdev->max_mtu = ETH_MAX_MTU; 65762306a36Sopenharmony_ci netdev->type = ARPHRD_PPP; 65862306a36Sopenharmony_ci netdev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; 65962306a36Sopenharmony_ci netdev->tx_queue_len = 100; 66062306a36Sopenharmony_ci INIT_LIST_HEAD(&brdev->brvccs); 66162306a36Sopenharmony_ci} 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_cistatic int br2684_create(void __user *arg) 66462306a36Sopenharmony_ci{ 66562306a36Sopenharmony_ci int err; 66662306a36Sopenharmony_ci struct net_device *netdev; 66762306a36Sopenharmony_ci struct br2684_dev *brdev; 66862306a36Sopenharmony_ci struct atm_newif_br2684 ni; 66962306a36Sopenharmony_ci enum br2684_payload payload; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci pr_debug("\n"); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci if (copy_from_user(&ni, arg, sizeof ni)) 67462306a36Sopenharmony_ci return -EFAULT; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci if (ni.media & BR2684_FLAG_ROUTED) 67762306a36Sopenharmony_ci payload = p_routed; 67862306a36Sopenharmony_ci else 67962306a36Sopenharmony_ci payload = p_bridged; 68062306a36Sopenharmony_ci ni.media &= 0xffff; /* strip flags */ 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (ni.media != BR2684_MEDIA_ETHERNET || ni.mtu != 1500) 68362306a36Sopenharmony_ci return -EINVAL; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci netdev = alloc_netdev(sizeof(struct br2684_dev), 68662306a36Sopenharmony_ci ni.ifname[0] ? ni.ifname : "nas%d", 68762306a36Sopenharmony_ci NET_NAME_UNKNOWN, 68862306a36Sopenharmony_ci (payload == p_routed) ? br2684_setup_routed : br2684_setup); 68962306a36Sopenharmony_ci if (!netdev) 69062306a36Sopenharmony_ci return -ENOMEM; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci brdev = BRPRIV(netdev); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci pr_debug("registered netdev %s\n", netdev->name); 69562306a36Sopenharmony_ci /* open, stop, do_ioctl ? */ 69662306a36Sopenharmony_ci err = register_netdev(netdev); 69762306a36Sopenharmony_ci if (err < 0) { 69862306a36Sopenharmony_ci pr_err("register_netdev failed\n"); 69962306a36Sopenharmony_ci free_netdev(netdev); 70062306a36Sopenharmony_ci return err; 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci write_lock_irq(&devs_lock); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci brdev->payload = payload; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci if (list_empty(&br2684_devs)) { 70862306a36Sopenharmony_ci /* 1st br2684 device */ 70962306a36Sopenharmony_ci brdev->number = 1; 71062306a36Sopenharmony_ci } else 71162306a36Sopenharmony_ci brdev->number = BRPRIV(list_entry_brdev(br2684_devs.prev))->number + 1; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci list_add_tail(&brdev->br2684_devs, &br2684_devs); 71462306a36Sopenharmony_ci write_unlock_irq(&devs_lock); 71562306a36Sopenharmony_ci return 0; 71662306a36Sopenharmony_ci} 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci/* 71962306a36Sopenharmony_ci * This handles ioctls actually performed on our vcc - we must return 72062306a36Sopenharmony_ci * -ENOIOCTLCMD for any unrecognized ioctl 72162306a36Sopenharmony_ci */ 72262306a36Sopenharmony_cistatic int br2684_ioctl(struct socket *sock, unsigned int cmd, 72362306a36Sopenharmony_ci unsigned long arg) 72462306a36Sopenharmony_ci{ 72562306a36Sopenharmony_ci struct atm_vcc *atmvcc = ATM_SD(sock); 72662306a36Sopenharmony_ci void __user *argp = (void __user *)arg; 72762306a36Sopenharmony_ci atm_backend_t b; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci int err; 73062306a36Sopenharmony_ci switch (cmd) { 73162306a36Sopenharmony_ci case ATM_SETBACKEND: 73262306a36Sopenharmony_ci case ATM_NEWBACKENDIF: 73362306a36Sopenharmony_ci err = get_user(b, (atm_backend_t __user *) argp); 73462306a36Sopenharmony_ci if (err) 73562306a36Sopenharmony_ci return -EFAULT; 73662306a36Sopenharmony_ci if (b != ATM_BACKEND_BR2684) 73762306a36Sopenharmony_ci return -ENOIOCTLCMD; 73862306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 73962306a36Sopenharmony_ci return -EPERM; 74062306a36Sopenharmony_ci if (cmd == ATM_SETBACKEND) { 74162306a36Sopenharmony_ci if (sock->state != SS_CONNECTED) 74262306a36Sopenharmony_ci return -EINVAL; 74362306a36Sopenharmony_ci return br2684_regvcc(atmvcc, argp); 74462306a36Sopenharmony_ci } else { 74562306a36Sopenharmony_ci return br2684_create(argp); 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci#ifdef CONFIG_ATM_BR2684_IPFILTER 74862306a36Sopenharmony_ci case BR2684_SETFILT: 74962306a36Sopenharmony_ci if (atmvcc->push != br2684_push) 75062306a36Sopenharmony_ci return -ENOIOCTLCMD; 75162306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 75262306a36Sopenharmony_ci return -EPERM; 75362306a36Sopenharmony_ci err = br2684_setfilt(atmvcc, argp); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci return err; 75662306a36Sopenharmony_ci#endif /* CONFIG_ATM_BR2684_IPFILTER */ 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci return -ENOIOCTLCMD; 75962306a36Sopenharmony_ci} 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_cistatic struct atm_ioctl br2684_ioctl_ops = { 76262306a36Sopenharmony_ci .owner = THIS_MODULE, 76362306a36Sopenharmony_ci .ioctl = br2684_ioctl, 76462306a36Sopenharmony_ci}; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 76762306a36Sopenharmony_cistatic void *br2684_seq_start(struct seq_file *seq, loff_t * pos) 76862306a36Sopenharmony_ci __acquires(devs_lock) 76962306a36Sopenharmony_ci{ 77062306a36Sopenharmony_ci read_lock(&devs_lock); 77162306a36Sopenharmony_ci return seq_list_start(&br2684_devs, *pos); 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_cistatic void *br2684_seq_next(struct seq_file *seq, void *v, loff_t * pos) 77562306a36Sopenharmony_ci{ 77662306a36Sopenharmony_ci return seq_list_next(v, &br2684_devs, pos); 77762306a36Sopenharmony_ci} 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_cistatic void br2684_seq_stop(struct seq_file *seq, void *v) 78062306a36Sopenharmony_ci __releases(devs_lock) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci read_unlock(&devs_lock); 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_cistatic int br2684_seq_show(struct seq_file *seq, void *v) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci const struct br2684_dev *brdev = list_entry(v, struct br2684_dev, 78862306a36Sopenharmony_ci br2684_devs); 78962306a36Sopenharmony_ci const struct net_device *net_dev = brdev->net_dev; 79062306a36Sopenharmony_ci const struct br2684_vcc *brvcc; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci seq_printf(seq, "dev %.16s: num=%d, mac=%pM (%s)\n", 79362306a36Sopenharmony_ci net_dev->name, 79462306a36Sopenharmony_ci brdev->number, 79562306a36Sopenharmony_ci net_dev->dev_addr, 79662306a36Sopenharmony_ci brdev->mac_was_set ? "set" : "auto"); 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci list_for_each_entry(brvcc, &brdev->brvccs, brvccs) { 79962306a36Sopenharmony_ci seq_printf(seq, " vcc %d.%d.%d: encaps=%s payload=%s" 80062306a36Sopenharmony_ci ", failed copies %u/%u" 80162306a36Sopenharmony_ci "\n", brvcc->atmvcc->dev->number, 80262306a36Sopenharmony_ci brvcc->atmvcc->vpi, brvcc->atmvcc->vci, 80362306a36Sopenharmony_ci (brvcc->encaps == e_llc) ? "LLC" : "VC", 80462306a36Sopenharmony_ci (brdev->payload == p_bridged) ? "bridged" : "routed", 80562306a36Sopenharmony_ci brvcc->copies_failed, brvcc->copies_needed); 80662306a36Sopenharmony_ci#ifdef CONFIG_ATM_BR2684_IPFILTER 80762306a36Sopenharmony_ci if (brvcc->filter.netmask != 0) 80862306a36Sopenharmony_ci seq_printf(seq, " filter=%pI4/%pI4\n", 80962306a36Sopenharmony_ci &brvcc->filter.prefix, 81062306a36Sopenharmony_ci &brvcc->filter.netmask); 81162306a36Sopenharmony_ci#endif /* CONFIG_ATM_BR2684_IPFILTER */ 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci return 0; 81462306a36Sopenharmony_ci} 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_cistatic const struct seq_operations br2684_seq_ops = { 81762306a36Sopenharmony_ci .start = br2684_seq_start, 81862306a36Sopenharmony_ci .next = br2684_seq_next, 81962306a36Sopenharmony_ci .stop = br2684_seq_stop, 82062306a36Sopenharmony_ci .show = br2684_seq_show, 82162306a36Sopenharmony_ci}; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ciextern struct proc_dir_entry *atm_proc_root; /* from proc.c */ 82462306a36Sopenharmony_ci#endif /* CONFIG_PROC_FS */ 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_cistatic int __init br2684_init(void) 82762306a36Sopenharmony_ci{ 82862306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 82962306a36Sopenharmony_ci struct proc_dir_entry *p; 83062306a36Sopenharmony_ci p = proc_create_seq("br2684", 0, atm_proc_root, &br2684_seq_ops); 83162306a36Sopenharmony_ci if (p == NULL) 83262306a36Sopenharmony_ci return -ENOMEM; 83362306a36Sopenharmony_ci#endif 83462306a36Sopenharmony_ci register_atm_ioctl(&br2684_ioctl_ops); 83562306a36Sopenharmony_ci register_atmdevice_notifier(&atm_dev_notifier); 83662306a36Sopenharmony_ci return 0; 83762306a36Sopenharmony_ci} 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_cistatic void __exit br2684_exit(void) 84062306a36Sopenharmony_ci{ 84162306a36Sopenharmony_ci struct net_device *net_dev; 84262306a36Sopenharmony_ci struct br2684_dev *brdev; 84362306a36Sopenharmony_ci struct br2684_vcc *brvcc; 84462306a36Sopenharmony_ci deregister_atm_ioctl(&br2684_ioctl_ops); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 84762306a36Sopenharmony_ci remove_proc_entry("br2684", atm_proc_root); 84862306a36Sopenharmony_ci#endif 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci unregister_atmdevice_notifier(&atm_dev_notifier); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci while (!list_empty(&br2684_devs)) { 85462306a36Sopenharmony_ci net_dev = list_entry_brdev(br2684_devs.next); 85562306a36Sopenharmony_ci brdev = BRPRIV(net_dev); 85662306a36Sopenharmony_ci while (!list_empty(&brdev->brvccs)) { 85762306a36Sopenharmony_ci brvcc = list_entry_brvcc(brdev->brvccs.next); 85862306a36Sopenharmony_ci br2684_close_vcc(brvcc); 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci list_del(&brdev->br2684_devs); 86262306a36Sopenharmony_ci unregister_netdev(net_dev); 86362306a36Sopenharmony_ci free_netdev(net_dev); 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci} 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_cimodule_init(br2684_init); 86862306a36Sopenharmony_cimodule_exit(br2684_exit); 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ciMODULE_AUTHOR("Marcell GAL"); 87162306a36Sopenharmony_ciMODULE_DESCRIPTION("RFC2684 bridged protocols over ATM/AAL5"); 87262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 873