18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Ethernet netdevice using ATM AAL5 as underlying carrier 48c2ecf20Sopenharmony_ci * (RFC1483 obsoleted by RFC2684) for Linux 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Authors: Marcell GAL, 2000, XDSL Ltd, Hungary 78c2ecf20Sopenharmony_ci * Eric Kinzie, 2006-2007, US Naval Research Laboratory 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/list.h> 168c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 178c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 188c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 198c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 208c2ecf20Sopenharmony_ci#include <linux/ip.h> 218c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <net/arp.h> 248c2ecf20Sopenharmony_ci#include <linux/atm.h> 258c2ecf20Sopenharmony_ci#include <linux/atmdev.h> 268c2ecf20Sopenharmony_ci#include <linux/capability.h> 278c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include <linux/atmbr2684.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include "common.h" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic void skb_debug(const struct sk_buff *skb) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci#ifdef SKB_DEBUG 368c2ecf20Sopenharmony_ci#define NUM2PRINT 50 378c2ecf20Sopenharmony_ci print_hex_dump(KERN_DEBUG, "br2684: skb: ", DUMP_OFFSET, 388c2ecf20Sopenharmony_ci 16, 1, skb->data, min(NUM2PRINT, skb->len), true); 398c2ecf20Sopenharmony_ci#endif 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define BR2684_ETHERTYPE_LEN 2 438c2ecf20Sopenharmony_ci#define BR2684_PAD_LEN 2 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define LLC 0xaa, 0xaa, 0x03 468c2ecf20Sopenharmony_ci#define SNAP_BRIDGED 0x00, 0x80, 0xc2 478c2ecf20Sopenharmony_ci#define SNAP_ROUTED 0x00, 0x00, 0x00 488c2ecf20Sopenharmony_ci#define PID_ETHERNET 0x00, 0x07 498c2ecf20Sopenharmony_ci#define ETHERTYPE_IPV4 0x08, 0x00 508c2ecf20Sopenharmony_ci#define ETHERTYPE_IPV6 0x86, 0xdd 518c2ecf20Sopenharmony_ci#define PAD_BRIDGED 0x00, 0x00 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic const unsigned char ethertype_ipv4[] = { ETHERTYPE_IPV4 }; 548c2ecf20Sopenharmony_cistatic const unsigned char ethertype_ipv6[] = { ETHERTYPE_IPV6 }; 558c2ecf20Sopenharmony_cistatic const unsigned char llc_oui_pid_pad[] = 568c2ecf20Sopenharmony_ci { LLC, SNAP_BRIDGED, PID_ETHERNET, PAD_BRIDGED }; 578c2ecf20Sopenharmony_cistatic const unsigned char pad[] = { PAD_BRIDGED }; 588c2ecf20Sopenharmony_cistatic const unsigned char llc_oui_ipv4[] = { LLC, SNAP_ROUTED, ETHERTYPE_IPV4 }; 598c2ecf20Sopenharmony_cistatic const unsigned char llc_oui_ipv6[] = { LLC, SNAP_ROUTED, ETHERTYPE_IPV6 }; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cienum br2684_encaps { 628c2ecf20Sopenharmony_ci e_vc = BR2684_ENCAPS_VC, 638c2ecf20Sopenharmony_ci e_llc = BR2684_ENCAPS_LLC, 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistruct br2684_vcc { 678c2ecf20Sopenharmony_ci struct atm_vcc *atmvcc; 688c2ecf20Sopenharmony_ci struct net_device *device; 698c2ecf20Sopenharmony_ci /* keep old push, pop functions for chaining */ 708c2ecf20Sopenharmony_ci void (*old_push)(struct atm_vcc *vcc, struct sk_buff *skb); 718c2ecf20Sopenharmony_ci void (*old_pop)(struct atm_vcc *vcc, struct sk_buff *skb); 728c2ecf20Sopenharmony_ci void (*old_release_cb)(struct atm_vcc *vcc); 738c2ecf20Sopenharmony_ci struct module *old_owner; 748c2ecf20Sopenharmony_ci enum br2684_encaps encaps; 758c2ecf20Sopenharmony_ci struct list_head brvccs; 768c2ecf20Sopenharmony_ci#ifdef CONFIG_ATM_BR2684_IPFILTER 778c2ecf20Sopenharmony_ci struct br2684_filter filter; 788c2ecf20Sopenharmony_ci#endif /* CONFIG_ATM_BR2684_IPFILTER */ 798c2ecf20Sopenharmony_ci unsigned int copies_needed, copies_failed; 808c2ecf20Sopenharmony_ci atomic_t qspace; 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistruct br2684_dev { 848c2ecf20Sopenharmony_ci struct net_device *net_dev; 858c2ecf20Sopenharmony_ci struct list_head br2684_devs; 868c2ecf20Sopenharmony_ci int number; 878c2ecf20Sopenharmony_ci struct list_head brvccs; /* one device <=> one vcc (before xmas) */ 888c2ecf20Sopenharmony_ci int mac_was_set; 898c2ecf20Sopenharmony_ci enum br2684_payload payload; 908c2ecf20Sopenharmony_ci}; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* 938c2ecf20Sopenharmony_ci * This lock should be held for writing any time the list of devices or 948c2ecf20Sopenharmony_ci * their attached vcc's could be altered. It should be held for reading 958c2ecf20Sopenharmony_ci * any time these are being queried. Note that we sometimes need to 968c2ecf20Sopenharmony_ci * do read-locking under interrupt context, so write locking must block 978c2ecf20Sopenharmony_ci * the current CPU's interrupts 988c2ecf20Sopenharmony_ci */ 998c2ecf20Sopenharmony_cistatic DEFINE_RWLOCK(devs_lock); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic LIST_HEAD(br2684_devs); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic inline struct br2684_dev *BRPRIV(const struct net_device *net_dev) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci return netdev_priv(net_dev); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic inline struct net_device *list_entry_brdev(const struct list_head *le) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci return list_entry(le, struct br2684_dev, br2684_devs)->net_dev; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic inline struct br2684_vcc *BR2684_VCC(const struct atm_vcc *atmvcc) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci return (struct br2684_vcc *)(atmvcc->user_back); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic inline struct br2684_vcc *list_entry_brvcc(const struct list_head *le) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci return list_entry(le, struct br2684_vcc, brvccs); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci/* Caller should hold read_lock(&devs_lock) */ 1248c2ecf20Sopenharmony_cistatic struct net_device *br2684_find_dev(const struct br2684_if_spec *s) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct list_head *lh; 1278c2ecf20Sopenharmony_ci struct net_device *net_dev; 1288c2ecf20Sopenharmony_ci switch (s->method) { 1298c2ecf20Sopenharmony_ci case BR2684_FIND_BYNUM: 1308c2ecf20Sopenharmony_ci list_for_each(lh, &br2684_devs) { 1318c2ecf20Sopenharmony_ci net_dev = list_entry_brdev(lh); 1328c2ecf20Sopenharmony_ci if (BRPRIV(net_dev)->number == s->spec.devnum) 1338c2ecf20Sopenharmony_ci return net_dev; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci break; 1368c2ecf20Sopenharmony_ci case BR2684_FIND_BYIFNAME: 1378c2ecf20Sopenharmony_ci list_for_each(lh, &br2684_devs) { 1388c2ecf20Sopenharmony_ci net_dev = list_entry_brdev(lh); 1398c2ecf20Sopenharmony_ci if (!strncmp(net_dev->name, s->spec.ifname, IFNAMSIZ)) 1408c2ecf20Sopenharmony_ci return net_dev; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci break; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci return NULL; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic int atm_dev_event(struct notifier_block *this, unsigned long event, 1488c2ecf20Sopenharmony_ci void *arg) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct atm_dev *atm_dev = arg; 1518c2ecf20Sopenharmony_ci struct list_head *lh; 1528c2ecf20Sopenharmony_ci struct net_device *net_dev; 1538c2ecf20Sopenharmony_ci struct br2684_vcc *brvcc; 1548c2ecf20Sopenharmony_ci struct atm_vcc *atm_vcc; 1558c2ecf20Sopenharmony_ci unsigned long flags; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci pr_debug("event=%ld dev=%p\n", event, atm_dev); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci read_lock_irqsave(&devs_lock, flags); 1608c2ecf20Sopenharmony_ci list_for_each(lh, &br2684_devs) { 1618c2ecf20Sopenharmony_ci net_dev = list_entry_brdev(lh); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci list_for_each_entry(brvcc, &BRPRIV(net_dev)->brvccs, brvccs) { 1648c2ecf20Sopenharmony_ci atm_vcc = brvcc->atmvcc; 1658c2ecf20Sopenharmony_ci if (atm_vcc && brvcc->atmvcc->dev == atm_dev) { 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (atm_vcc->dev->signal == ATM_PHY_SIG_LOST) 1688c2ecf20Sopenharmony_ci netif_carrier_off(net_dev); 1698c2ecf20Sopenharmony_ci else 1708c2ecf20Sopenharmony_ci netif_carrier_on(net_dev); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci read_unlock_irqrestore(&devs_lock, flags); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci return NOTIFY_DONE; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic struct notifier_block atm_dev_notifier = { 1818c2ecf20Sopenharmony_ci .notifier_call = atm_dev_event, 1828c2ecf20Sopenharmony_ci}; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci/* chained vcc->pop function. Check if we should wake the netif_queue */ 1858c2ecf20Sopenharmony_cistatic void br2684_pop(struct atm_vcc *vcc, struct sk_buff *skb) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct br2684_vcc *brvcc = BR2684_VCC(vcc); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci pr_debug("(vcc %p ; net_dev %p )\n", vcc, brvcc->device); 1908c2ecf20Sopenharmony_ci brvcc->old_pop(vcc, skb); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* If the queue space just went up from zero, wake */ 1938c2ecf20Sopenharmony_ci if (atomic_inc_return(&brvcc->qspace) == 1) 1948c2ecf20Sopenharmony_ci netif_wake_queue(brvcc->device); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci/* 1988c2ecf20Sopenharmony_ci * Send a packet out a particular vcc. Not to useful right now, but paves 1998c2ecf20Sopenharmony_ci * the way for multiple vcc's per itf. Returns true if we can send, 2008c2ecf20Sopenharmony_ci * otherwise false 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_cistatic int br2684_xmit_vcc(struct sk_buff *skb, struct net_device *dev, 2038c2ecf20Sopenharmony_ci struct br2684_vcc *brvcc) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci struct br2684_dev *brdev = BRPRIV(dev); 2068c2ecf20Sopenharmony_ci struct atm_vcc *atmvcc; 2078c2ecf20Sopenharmony_ci int minheadroom = (brvcc->encaps == e_llc) ? 2088c2ecf20Sopenharmony_ci ((brdev->payload == p_bridged) ? 2098c2ecf20Sopenharmony_ci sizeof(llc_oui_pid_pad) : sizeof(llc_oui_ipv4)) : 2108c2ecf20Sopenharmony_ci ((brdev->payload == p_bridged) ? BR2684_PAD_LEN : 0); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (skb_headroom(skb) < minheadroom) { 2138c2ecf20Sopenharmony_ci struct sk_buff *skb2 = skb_realloc_headroom(skb, minheadroom); 2148c2ecf20Sopenharmony_ci brvcc->copies_needed++; 2158c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 2168c2ecf20Sopenharmony_ci if (skb2 == NULL) { 2178c2ecf20Sopenharmony_ci brvcc->copies_failed++; 2188c2ecf20Sopenharmony_ci return 0; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci skb = skb2; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (brvcc->encaps == e_llc) { 2248c2ecf20Sopenharmony_ci if (brdev->payload == p_bridged) { 2258c2ecf20Sopenharmony_ci skb_push(skb, sizeof(llc_oui_pid_pad)); 2268c2ecf20Sopenharmony_ci skb_copy_to_linear_data(skb, llc_oui_pid_pad, 2278c2ecf20Sopenharmony_ci sizeof(llc_oui_pid_pad)); 2288c2ecf20Sopenharmony_ci } else if (brdev->payload == p_routed) { 2298c2ecf20Sopenharmony_ci unsigned short prot = ntohs(skb->protocol); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci skb_push(skb, sizeof(llc_oui_ipv4)); 2328c2ecf20Sopenharmony_ci switch (prot) { 2338c2ecf20Sopenharmony_ci case ETH_P_IP: 2348c2ecf20Sopenharmony_ci skb_copy_to_linear_data(skb, llc_oui_ipv4, 2358c2ecf20Sopenharmony_ci sizeof(llc_oui_ipv4)); 2368c2ecf20Sopenharmony_ci break; 2378c2ecf20Sopenharmony_ci case ETH_P_IPV6: 2388c2ecf20Sopenharmony_ci skb_copy_to_linear_data(skb, llc_oui_ipv6, 2398c2ecf20Sopenharmony_ci sizeof(llc_oui_ipv6)); 2408c2ecf20Sopenharmony_ci break; 2418c2ecf20Sopenharmony_ci default: 2428c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 2438c2ecf20Sopenharmony_ci return 0; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci } else { /* e_vc */ 2478c2ecf20Sopenharmony_ci if (brdev->payload == p_bridged) { 2488c2ecf20Sopenharmony_ci skb_push(skb, 2); 2498c2ecf20Sopenharmony_ci memset(skb->data, 0, 2); 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci skb_debug(skb); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci ATM_SKB(skb)->vcc = atmvcc = brvcc->atmvcc; 2558c2ecf20Sopenharmony_ci pr_debug("atm_skb(%p)->vcc(%p)->dev(%p)\n", skb, atmvcc, atmvcc->dev); 2568c2ecf20Sopenharmony_ci atm_account_tx(atmvcc, skb); 2578c2ecf20Sopenharmony_ci dev->stats.tx_packets++; 2588c2ecf20Sopenharmony_ci dev->stats.tx_bytes += skb->len; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (atomic_dec_return(&brvcc->qspace) < 1) { 2618c2ecf20Sopenharmony_ci /* No more please! */ 2628c2ecf20Sopenharmony_ci netif_stop_queue(brvcc->device); 2638c2ecf20Sopenharmony_ci /* We might have raced with br2684_pop() */ 2648c2ecf20Sopenharmony_ci if (unlikely(atomic_read(&brvcc->qspace) > 0)) 2658c2ecf20Sopenharmony_ci netif_wake_queue(brvcc->device); 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* If this fails immediately, the skb will be freed and br2684_pop() 2698c2ecf20Sopenharmony_ci will wake the queue if appropriate. Just return an error so that 2708c2ecf20Sopenharmony_ci the stats are updated correctly */ 2718c2ecf20Sopenharmony_ci return !atmvcc->send(atmvcc, skb); 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic void br2684_release_cb(struct atm_vcc *atmvcc) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct br2684_vcc *brvcc = BR2684_VCC(atmvcc); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (atomic_read(&brvcc->qspace) > 0) 2798c2ecf20Sopenharmony_ci netif_wake_queue(brvcc->device); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (brvcc->old_release_cb) 2828c2ecf20Sopenharmony_ci brvcc->old_release_cb(atmvcc); 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic inline struct br2684_vcc *pick_outgoing_vcc(const struct sk_buff *skb, 2868c2ecf20Sopenharmony_ci const struct br2684_dev *brdev) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci return list_empty(&brdev->brvccs) ? NULL : list_entry_brvcc(brdev->brvccs.next); /* 1 vcc/dev right now */ 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic netdev_tx_t br2684_start_xmit(struct sk_buff *skb, 2928c2ecf20Sopenharmony_ci struct net_device *dev) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci struct br2684_dev *brdev = BRPRIV(dev); 2958c2ecf20Sopenharmony_ci struct br2684_vcc *brvcc; 2968c2ecf20Sopenharmony_ci struct atm_vcc *atmvcc; 2978c2ecf20Sopenharmony_ci netdev_tx_t ret = NETDEV_TX_OK; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci pr_debug("skb_dst(skb)=%p\n", skb_dst(skb)); 3008c2ecf20Sopenharmony_ci read_lock(&devs_lock); 3018c2ecf20Sopenharmony_ci brvcc = pick_outgoing_vcc(skb, brdev); 3028c2ecf20Sopenharmony_ci if (brvcc == NULL) { 3038c2ecf20Sopenharmony_ci pr_debug("no vcc attached to dev %s\n", dev->name); 3048c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 3058c2ecf20Sopenharmony_ci dev->stats.tx_carrier_errors++; 3068c2ecf20Sopenharmony_ci /* netif_stop_queue(dev); */ 3078c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 3088c2ecf20Sopenharmony_ci goto out_devs; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci atmvcc = brvcc->atmvcc; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci bh_lock_sock(sk_atm(atmvcc)); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (test_bit(ATM_VF_RELEASED, &atmvcc->flags) || 3158c2ecf20Sopenharmony_ci test_bit(ATM_VF_CLOSE, &atmvcc->flags) || 3168c2ecf20Sopenharmony_ci !test_bit(ATM_VF_READY, &atmvcc->flags)) { 3178c2ecf20Sopenharmony_ci dev->stats.tx_dropped++; 3188c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 3198c2ecf20Sopenharmony_ci goto out; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (sock_owned_by_user(sk_atm(atmvcc))) { 3238c2ecf20Sopenharmony_ci netif_stop_queue(brvcc->device); 3248c2ecf20Sopenharmony_ci ret = NETDEV_TX_BUSY; 3258c2ecf20Sopenharmony_ci goto out; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (!br2684_xmit_vcc(skb, dev, brvcc)) { 3298c2ecf20Sopenharmony_ci /* 3308c2ecf20Sopenharmony_ci * We should probably use netif_*_queue() here, but that 3318c2ecf20Sopenharmony_ci * involves added complication. We need to walk before 3328c2ecf20Sopenharmony_ci * we can run. 3338c2ecf20Sopenharmony_ci * 3348c2ecf20Sopenharmony_ci * Don't free here! this pointer might be no longer valid! 3358c2ecf20Sopenharmony_ci */ 3368c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 3378c2ecf20Sopenharmony_ci dev->stats.tx_fifo_errors++; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci out: 3408c2ecf20Sopenharmony_ci bh_unlock_sock(sk_atm(atmvcc)); 3418c2ecf20Sopenharmony_ci out_devs: 3428c2ecf20Sopenharmony_ci read_unlock(&devs_lock); 3438c2ecf20Sopenharmony_ci return ret; 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci/* 3478c2ecf20Sopenharmony_ci * We remember when the MAC gets set, so we don't override it later with 3488c2ecf20Sopenharmony_ci * the ESI of the ATM card of the first VC 3498c2ecf20Sopenharmony_ci */ 3508c2ecf20Sopenharmony_cistatic int br2684_mac_addr(struct net_device *dev, void *p) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci int err = eth_mac_addr(dev, p); 3538c2ecf20Sopenharmony_ci if (!err) 3548c2ecf20Sopenharmony_ci BRPRIV(dev)->mac_was_set = 1; 3558c2ecf20Sopenharmony_ci return err; 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci#ifdef CONFIG_ATM_BR2684_IPFILTER 3598c2ecf20Sopenharmony_ci/* this IOCTL is experimental. */ 3608c2ecf20Sopenharmony_cistatic int br2684_setfilt(struct atm_vcc *atmvcc, void __user * arg) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci struct br2684_vcc *brvcc; 3638c2ecf20Sopenharmony_ci struct br2684_filter_set fs; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci if (copy_from_user(&fs, arg, sizeof fs)) 3668c2ecf20Sopenharmony_ci return -EFAULT; 3678c2ecf20Sopenharmony_ci if (fs.ifspec.method != BR2684_FIND_BYNOTHING) { 3688c2ecf20Sopenharmony_ci /* 3698c2ecf20Sopenharmony_ci * This is really a per-vcc thing, but we can also search 3708c2ecf20Sopenharmony_ci * by device. 3718c2ecf20Sopenharmony_ci */ 3728c2ecf20Sopenharmony_ci struct br2684_dev *brdev; 3738c2ecf20Sopenharmony_ci read_lock(&devs_lock); 3748c2ecf20Sopenharmony_ci brdev = BRPRIV(br2684_find_dev(&fs.ifspec)); 3758c2ecf20Sopenharmony_ci if (brdev == NULL || list_empty(&brdev->brvccs) || 3768c2ecf20Sopenharmony_ci brdev->brvccs.next != brdev->brvccs.prev) /* >1 VCC */ 3778c2ecf20Sopenharmony_ci brvcc = NULL; 3788c2ecf20Sopenharmony_ci else 3798c2ecf20Sopenharmony_ci brvcc = list_entry_brvcc(brdev->brvccs.next); 3808c2ecf20Sopenharmony_ci read_unlock(&devs_lock); 3818c2ecf20Sopenharmony_ci if (brvcc == NULL) 3828c2ecf20Sopenharmony_ci return -ESRCH; 3838c2ecf20Sopenharmony_ci } else 3848c2ecf20Sopenharmony_ci brvcc = BR2684_VCC(atmvcc); 3858c2ecf20Sopenharmony_ci memcpy(&brvcc->filter, &fs.filter, sizeof(brvcc->filter)); 3868c2ecf20Sopenharmony_ci return 0; 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci/* Returns 1 if packet should be dropped */ 3908c2ecf20Sopenharmony_cistatic inline int 3918c2ecf20Sopenharmony_cipacket_fails_filter(__be16 type, struct br2684_vcc *brvcc, struct sk_buff *skb) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci if (brvcc->filter.netmask == 0) 3948c2ecf20Sopenharmony_ci return 0; /* no filter in place */ 3958c2ecf20Sopenharmony_ci if (type == htons(ETH_P_IP) && 3968c2ecf20Sopenharmony_ci (((struct iphdr *)(skb->data))->daddr & brvcc->filter. 3978c2ecf20Sopenharmony_ci netmask) == brvcc->filter.prefix) 3988c2ecf20Sopenharmony_ci return 0; 3998c2ecf20Sopenharmony_ci if (type == htons(ETH_P_ARP)) 4008c2ecf20Sopenharmony_ci return 0; 4018c2ecf20Sopenharmony_ci /* 4028c2ecf20Sopenharmony_ci * TODO: we should probably filter ARPs too.. don't want to have 4038c2ecf20Sopenharmony_ci * them returning values that don't make sense, or is that ok? 4048c2ecf20Sopenharmony_ci */ 4058c2ecf20Sopenharmony_ci return 1; /* drop */ 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci#endif /* CONFIG_ATM_BR2684_IPFILTER */ 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cistatic void br2684_close_vcc(struct br2684_vcc *brvcc) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci pr_debug("removing VCC %p from dev %p\n", brvcc, brvcc->device); 4128c2ecf20Sopenharmony_ci write_lock_irq(&devs_lock); 4138c2ecf20Sopenharmony_ci list_del(&brvcc->brvccs); 4148c2ecf20Sopenharmony_ci write_unlock_irq(&devs_lock); 4158c2ecf20Sopenharmony_ci brvcc->atmvcc->user_back = NULL; /* what about vcc->recvq ??? */ 4168c2ecf20Sopenharmony_ci brvcc->atmvcc->release_cb = brvcc->old_release_cb; 4178c2ecf20Sopenharmony_ci brvcc->old_push(brvcc->atmvcc, NULL); /* pass on the bad news */ 4188c2ecf20Sopenharmony_ci module_put(brvcc->old_owner); 4198c2ecf20Sopenharmony_ci kfree(brvcc); 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci/* when AAL5 PDU comes in: */ 4238c2ecf20Sopenharmony_cistatic void br2684_push(struct atm_vcc *atmvcc, struct sk_buff *skb) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci struct br2684_vcc *brvcc = BR2684_VCC(atmvcc); 4268c2ecf20Sopenharmony_ci struct net_device *net_dev = brvcc->device; 4278c2ecf20Sopenharmony_ci struct br2684_dev *brdev = BRPRIV(net_dev); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci pr_debug("\n"); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (unlikely(skb == NULL)) { 4328c2ecf20Sopenharmony_ci /* skb==NULL means VCC is being destroyed */ 4338c2ecf20Sopenharmony_ci br2684_close_vcc(brvcc); 4348c2ecf20Sopenharmony_ci if (list_empty(&brdev->brvccs)) { 4358c2ecf20Sopenharmony_ci write_lock_irq(&devs_lock); 4368c2ecf20Sopenharmony_ci list_del(&brdev->br2684_devs); 4378c2ecf20Sopenharmony_ci write_unlock_irq(&devs_lock); 4388c2ecf20Sopenharmony_ci unregister_netdev(net_dev); 4398c2ecf20Sopenharmony_ci free_netdev(net_dev); 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci return; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci skb_debug(skb); 4458c2ecf20Sopenharmony_ci atm_return(atmvcc, skb->truesize); 4468c2ecf20Sopenharmony_ci pr_debug("skb from brdev %p\n", brdev); 4478c2ecf20Sopenharmony_ci if (brvcc->encaps == e_llc) { 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci if (skb->len > 7 && skb->data[7] == 0x01) 4508c2ecf20Sopenharmony_ci __skb_trim(skb, skb->len - 4); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci /* accept packets that have "ipv[46]" in the snap header */ 4538c2ecf20Sopenharmony_ci if ((skb->len >= (sizeof(llc_oui_ipv4))) && 4548c2ecf20Sopenharmony_ci (memcmp(skb->data, llc_oui_ipv4, 4558c2ecf20Sopenharmony_ci sizeof(llc_oui_ipv4) - BR2684_ETHERTYPE_LEN) == 0)) { 4568c2ecf20Sopenharmony_ci if (memcmp(skb->data + 6, ethertype_ipv6, 4578c2ecf20Sopenharmony_ci sizeof(ethertype_ipv6)) == 0) 4588c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_IPV6); 4598c2ecf20Sopenharmony_ci else if (memcmp(skb->data + 6, ethertype_ipv4, 4608c2ecf20Sopenharmony_ci sizeof(ethertype_ipv4)) == 0) 4618c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_IP); 4628c2ecf20Sopenharmony_ci else 4638c2ecf20Sopenharmony_ci goto error; 4648c2ecf20Sopenharmony_ci skb_pull(skb, sizeof(llc_oui_ipv4)); 4658c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 4668c2ecf20Sopenharmony_ci skb->pkt_type = PACKET_HOST; 4678c2ecf20Sopenharmony_ci /* 4688c2ecf20Sopenharmony_ci * Let us waste some time for checking the encapsulation. 4698c2ecf20Sopenharmony_ci * Note, that only 7 char is checked so frames with a valid FCS 4708c2ecf20Sopenharmony_ci * are also accepted (but FCS is not checked of course). 4718c2ecf20Sopenharmony_ci */ 4728c2ecf20Sopenharmony_ci } else if ((skb->len >= sizeof(llc_oui_pid_pad)) && 4738c2ecf20Sopenharmony_ci (memcmp(skb->data, llc_oui_pid_pad, 7) == 0)) { 4748c2ecf20Sopenharmony_ci skb_pull(skb, sizeof(llc_oui_pid_pad)); 4758c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, net_dev); 4768c2ecf20Sopenharmony_ci } else 4778c2ecf20Sopenharmony_ci goto error; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci } else { /* e_vc */ 4808c2ecf20Sopenharmony_ci if (brdev->payload == p_routed) { 4818c2ecf20Sopenharmony_ci struct iphdr *iph; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 4848c2ecf20Sopenharmony_ci iph = ip_hdr(skb); 4858c2ecf20Sopenharmony_ci if (iph->version == 4) 4868c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_IP); 4878c2ecf20Sopenharmony_ci else if (iph->version == 6) 4888c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_IPV6); 4898c2ecf20Sopenharmony_ci else 4908c2ecf20Sopenharmony_ci goto error; 4918c2ecf20Sopenharmony_ci skb->pkt_type = PACKET_HOST; 4928c2ecf20Sopenharmony_ci } else { /* p_bridged */ 4938c2ecf20Sopenharmony_ci /* first 2 chars should be 0 */ 4948c2ecf20Sopenharmony_ci if (memcmp(skb->data, pad, BR2684_PAD_LEN) != 0) 4958c2ecf20Sopenharmony_ci goto error; 4968c2ecf20Sopenharmony_ci skb_pull(skb, BR2684_PAD_LEN); 4978c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, net_dev); 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci#ifdef CONFIG_ATM_BR2684_IPFILTER 5028c2ecf20Sopenharmony_ci if (unlikely(packet_fails_filter(skb->protocol, brvcc, skb))) 5038c2ecf20Sopenharmony_ci goto dropped; 5048c2ecf20Sopenharmony_ci#endif /* CONFIG_ATM_BR2684_IPFILTER */ 5058c2ecf20Sopenharmony_ci skb->dev = net_dev; 5068c2ecf20Sopenharmony_ci ATM_SKB(skb)->vcc = atmvcc; /* needed ? */ 5078c2ecf20Sopenharmony_ci pr_debug("received packet's protocol: %x\n", ntohs(skb->protocol)); 5088c2ecf20Sopenharmony_ci skb_debug(skb); 5098c2ecf20Sopenharmony_ci /* sigh, interface is down? */ 5108c2ecf20Sopenharmony_ci if (unlikely(!(net_dev->flags & IFF_UP))) 5118c2ecf20Sopenharmony_ci goto dropped; 5128c2ecf20Sopenharmony_ci net_dev->stats.rx_packets++; 5138c2ecf20Sopenharmony_ci net_dev->stats.rx_bytes += skb->len; 5148c2ecf20Sopenharmony_ci memset(ATM_SKB(skb), 0, sizeof(struct atm_skb_data)); 5158c2ecf20Sopenharmony_ci netif_rx(skb); 5168c2ecf20Sopenharmony_ci return; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_cidropped: 5198c2ecf20Sopenharmony_ci net_dev->stats.rx_dropped++; 5208c2ecf20Sopenharmony_ci goto free_skb; 5218c2ecf20Sopenharmony_cierror: 5228c2ecf20Sopenharmony_ci net_dev->stats.rx_errors++; 5238c2ecf20Sopenharmony_cifree_skb: 5248c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci/* 5288c2ecf20Sopenharmony_ci * Assign a vcc to a dev 5298c2ecf20Sopenharmony_ci * Note: we do not have explicit unassign, but look at _push() 5308c2ecf20Sopenharmony_ci */ 5318c2ecf20Sopenharmony_cistatic int br2684_regvcc(struct atm_vcc *atmvcc, void __user * arg) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci struct br2684_vcc *brvcc; 5348c2ecf20Sopenharmony_ci struct br2684_dev *brdev; 5358c2ecf20Sopenharmony_ci struct net_device *net_dev; 5368c2ecf20Sopenharmony_ci struct atm_backend_br2684 be; 5378c2ecf20Sopenharmony_ci int err; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci if (copy_from_user(&be, arg, sizeof be)) 5408c2ecf20Sopenharmony_ci return -EFAULT; 5418c2ecf20Sopenharmony_ci brvcc = kzalloc(sizeof(struct br2684_vcc), GFP_KERNEL); 5428c2ecf20Sopenharmony_ci if (!brvcc) 5438c2ecf20Sopenharmony_ci return -ENOMEM; 5448c2ecf20Sopenharmony_ci /* 5458c2ecf20Sopenharmony_ci * Allow two packets in the ATM queue. One actually being sent, and one 5468c2ecf20Sopenharmony_ci * for the ATM 'TX done' handler to send. It shouldn't take long to get 5478c2ecf20Sopenharmony_ci * the next one from the netdev queue, when we need it. More than that 5488c2ecf20Sopenharmony_ci * would be bufferbloat. 5498c2ecf20Sopenharmony_ci */ 5508c2ecf20Sopenharmony_ci atomic_set(&brvcc->qspace, 2); 5518c2ecf20Sopenharmony_ci write_lock_irq(&devs_lock); 5528c2ecf20Sopenharmony_ci net_dev = br2684_find_dev(&be.ifspec); 5538c2ecf20Sopenharmony_ci if (net_dev == NULL) { 5548c2ecf20Sopenharmony_ci pr_err("tried to attach to non-existent device\n"); 5558c2ecf20Sopenharmony_ci err = -ENXIO; 5568c2ecf20Sopenharmony_ci goto error; 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci brdev = BRPRIV(net_dev); 5598c2ecf20Sopenharmony_ci if (atmvcc->push == NULL) { 5608c2ecf20Sopenharmony_ci err = -EBADFD; 5618c2ecf20Sopenharmony_ci goto error; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci if (!list_empty(&brdev->brvccs)) { 5648c2ecf20Sopenharmony_ci /* Only 1 VCC/dev right now */ 5658c2ecf20Sopenharmony_ci err = -EEXIST; 5668c2ecf20Sopenharmony_ci goto error; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci if (be.fcs_in != BR2684_FCSIN_NO || 5698c2ecf20Sopenharmony_ci be.fcs_out != BR2684_FCSOUT_NO || 5708c2ecf20Sopenharmony_ci be.fcs_auto || be.has_vpiid || be.send_padding || 5718c2ecf20Sopenharmony_ci (be.encaps != BR2684_ENCAPS_VC && 5728c2ecf20Sopenharmony_ci be.encaps != BR2684_ENCAPS_LLC) || 5738c2ecf20Sopenharmony_ci be.min_size != 0) { 5748c2ecf20Sopenharmony_ci err = -EINVAL; 5758c2ecf20Sopenharmony_ci goto error; 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci pr_debug("vcc=%p, encaps=%d, brvcc=%p\n", atmvcc, be.encaps, brvcc); 5788c2ecf20Sopenharmony_ci if (list_empty(&brdev->brvccs) && !brdev->mac_was_set) { 5798c2ecf20Sopenharmony_ci unsigned char *esi = atmvcc->dev->esi; 5808c2ecf20Sopenharmony_ci if (esi[0] | esi[1] | esi[2] | esi[3] | esi[4] | esi[5]) 5818c2ecf20Sopenharmony_ci memcpy(net_dev->dev_addr, esi, net_dev->addr_len); 5828c2ecf20Sopenharmony_ci else 5838c2ecf20Sopenharmony_ci net_dev->dev_addr[2] = 1; 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci list_add(&brvcc->brvccs, &brdev->brvccs); 5868c2ecf20Sopenharmony_ci write_unlock_irq(&devs_lock); 5878c2ecf20Sopenharmony_ci brvcc->device = net_dev; 5888c2ecf20Sopenharmony_ci brvcc->atmvcc = atmvcc; 5898c2ecf20Sopenharmony_ci atmvcc->user_back = brvcc; 5908c2ecf20Sopenharmony_ci brvcc->encaps = (enum br2684_encaps)be.encaps; 5918c2ecf20Sopenharmony_ci brvcc->old_push = atmvcc->push; 5928c2ecf20Sopenharmony_ci brvcc->old_pop = atmvcc->pop; 5938c2ecf20Sopenharmony_ci brvcc->old_release_cb = atmvcc->release_cb; 5948c2ecf20Sopenharmony_ci brvcc->old_owner = atmvcc->owner; 5958c2ecf20Sopenharmony_ci barrier(); 5968c2ecf20Sopenharmony_ci atmvcc->push = br2684_push; 5978c2ecf20Sopenharmony_ci atmvcc->pop = br2684_pop; 5988c2ecf20Sopenharmony_ci atmvcc->release_cb = br2684_release_cb; 5998c2ecf20Sopenharmony_ci atmvcc->owner = THIS_MODULE; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci /* initialize netdev carrier state */ 6028c2ecf20Sopenharmony_ci if (atmvcc->dev->signal == ATM_PHY_SIG_LOST) 6038c2ecf20Sopenharmony_ci netif_carrier_off(net_dev); 6048c2ecf20Sopenharmony_ci else 6058c2ecf20Sopenharmony_ci netif_carrier_on(net_dev); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci __module_get(THIS_MODULE); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci /* re-process everything received between connection setup and 6108c2ecf20Sopenharmony_ci backend setup */ 6118c2ecf20Sopenharmony_ci vcc_process_recv_queue(atmvcc); 6128c2ecf20Sopenharmony_ci return 0; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cierror: 6158c2ecf20Sopenharmony_ci write_unlock_irq(&devs_lock); 6168c2ecf20Sopenharmony_ci kfree(brvcc); 6178c2ecf20Sopenharmony_ci return err; 6188c2ecf20Sopenharmony_ci} 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_cistatic const struct net_device_ops br2684_netdev_ops = { 6218c2ecf20Sopenharmony_ci .ndo_start_xmit = br2684_start_xmit, 6228c2ecf20Sopenharmony_ci .ndo_set_mac_address = br2684_mac_addr, 6238c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 6248c2ecf20Sopenharmony_ci}; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_cistatic const struct net_device_ops br2684_netdev_ops_routed = { 6278c2ecf20Sopenharmony_ci .ndo_start_xmit = br2684_start_xmit, 6288c2ecf20Sopenharmony_ci .ndo_set_mac_address = br2684_mac_addr, 6298c2ecf20Sopenharmony_ci}; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_cistatic void br2684_setup(struct net_device *netdev) 6328c2ecf20Sopenharmony_ci{ 6338c2ecf20Sopenharmony_ci struct br2684_dev *brdev = BRPRIV(netdev); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci ether_setup(netdev); 6368c2ecf20Sopenharmony_ci netdev->hard_header_len += sizeof(llc_oui_pid_pad); /* worst case */ 6378c2ecf20Sopenharmony_ci brdev->net_dev = netdev; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci netdev->netdev_ops = &br2684_netdev_ops; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&brdev->brvccs); 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_cistatic void br2684_setup_routed(struct net_device *netdev) 6458c2ecf20Sopenharmony_ci{ 6468c2ecf20Sopenharmony_ci struct br2684_dev *brdev = BRPRIV(netdev); 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci brdev->net_dev = netdev; 6498c2ecf20Sopenharmony_ci netdev->hard_header_len = sizeof(llc_oui_ipv4); /* worst case */ 6508c2ecf20Sopenharmony_ci netdev->netdev_ops = &br2684_netdev_ops_routed; 6518c2ecf20Sopenharmony_ci netdev->addr_len = 0; 6528c2ecf20Sopenharmony_ci netdev->mtu = ETH_DATA_LEN; 6538c2ecf20Sopenharmony_ci netdev->min_mtu = 0; 6548c2ecf20Sopenharmony_ci netdev->max_mtu = ETH_MAX_MTU; 6558c2ecf20Sopenharmony_ci netdev->type = ARPHRD_PPP; 6568c2ecf20Sopenharmony_ci netdev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; 6578c2ecf20Sopenharmony_ci netdev->tx_queue_len = 100; 6588c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&brdev->brvccs); 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_cistatic int br2684_create(void __user *arg) 6628c2ecf20Sopenharmony_ci{ 6638c2ecf20Sopenharmony_ci int err; 6648c2ecf20Sopenharmony_ci struct net_device *netdev; 6658c2ecf20Sopenharmony_ci struct br2684_dev *brdev; 6668c2ecf20Sopenharmony_ci struct atm_newif_br2684 ni; 6678c2ecf20Sopenharmony_ci enum br2684_payload payload; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci pr_debug("\n"); 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci if (copy_from_user(&ni, arg, sizeof ni)) 6728c2ecf20Sopenharmony_ci return -EFAULT; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci if (ni.media & BR2684_FLAG_ROUTED) 6758c2ecf20Sopenharmony_ci payload = p_routed; 6768c2ecf20Sopenharmony_ci else 6778c2ecf20Sopenharmony_ci payload = p_bridged; 6788c2ecf20Sopenharmony_ci ni.media &= 0xffff; /* strip flags */ 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci if (ni.media != BR2684_MEDIA_ETHERNET || ni.mtu != 1500) 6818c2ecf20Sopenharmony_ci return -EINVAL; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci netdev = alloc_netdev(sizeof(struct br2684_dev), 6848c2ecf20Sopenharmony_ci ni.ifname[0] ? ni.ifname : "nas%d", 6858c2ecf20Sopenharmony_ci NET_NAME_UNKNOWN, 6868c2ecf20Sopenharmony_ci (payload == p_routed) ? br2684_setup_routed : br2684_setup); 6878c2ecf20Sopenharmony_ci if (!netdev) 6888c2ecf20Sopenharmony_ci return -ENOMEM; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci brdev = BRPRIV(netdev); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci pr_debug("registered netdev %s\n", netdev->name); 6938c2ecf20Sopenharmony_ci /* open, stop, do_ioctl ? */ 6948c2ecf20Sopenharmony_ci err = register_netdev(netdev); 6958c2ecf20Sopenharmony_ci if (err < 0) { 6968c2ecf20Sopenharmony_ci pr_err("register_netdev failed\n"); 6978c2ecf20Sopenharmony_ci free_netdev(netdev); 6988c2ecf20Sopenharmony_ci return err; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci write_lock_irq(&devs_lock); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci brdev->payload = payload; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci if (list_empty(&br2684_devs)) { 7068c2ecf20Sopenharmony_ci /* 1st br2684 device */ 7078c2ecf20Sopenharmony_ci brdev->number = 1; 7088c2ecf20Sopenharmony_ci } else 7098c2ecf20Sopenharmony_ci brdev->number = BRPRIV(list_entry_brdev(br2684_devs.prev))->number + 1; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci list_add_tail(&brdev->br2684_devs, &br2684_devs); 7128c2ecf20Sopenharmony_ci write_unlock_irq(&devs_lock); 7138c2ecf20Sopenharmony_ci return 0; 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci/* 7178c2ecf20Sopenharmony_ci * This handles ioctls actually performed on our vcc - we must return 7188c2ecf20Sopenharmony_ci * -ENOIOCTLCMD for any unrecognized ioctl 7198c2ecf20Sopenharmony_ci */ 7208c2ecf20Sopenharmony_cistatic int br2684_ioctl(struct socket *sock, unsigned int cmd, 7218c2ecf20Sopenharmony_ci unsigned long arg) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci struct atm_vcc *atmvcc = ATM_SD(sock); 7248c2ecf20Sopenharmony_ci void __user *argp = (void __user *)arg; 7258c2ecf20Sopenharmony_ci atm_backend_t b; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci int err; 7288c2ecf20Sopenharmony_ci switch (cmd) { 7298c2ecf20Sopenharmony_ci case ATM_SETBACKEND: 7308c2ecf20Sopenharmony_ci case ATM_NEWBACKENDIF: 7318c2ecf20Sopenharmony_ci err = get_user(b, (atm_backend_t __user *) argp); 7328c2ecf20Sopenharmony_ci if (err) 7338c2ecf20Sopenharmony_ci return -EFAULT; 7348c2ecf20Sopenharmony_ci if (b != ATM_BACKEND_BR2684) 7358c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 7368c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 7378c2ecf20Sopenharmony_ci return -EPERM; 7388c2ecf20Sopenharmony_ci if (cmd == ATM_SETBACKEND) { 7398c2ecf20Sopenharmony_ci if (sock->state != SS_CONNECTED) 7408c2ecf20Sopenharmony_ci return -EINVAL; 7418c2ecf20Sopenharmony_ci return br2684_regvcc(atmvcc, argp); 7428c2ecf20Sopenharmony_ci } else { 7438c2ecf20Sopenharmony_ci return br2684_create(argp); 7448c2ecf20Sopenharmony_ci } 7458c2ecf20Sopenharmony_ci#ifdef CONFIG_ATM_BR2684_IPFILTER 7468c2ecf20Sopenharmony_ci case BR2684_SETFILT: 7478c2ecf20Sopenharmony_ci if (atmvcc->push != br2684_push) 7488c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 7498c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 7508c2ecf20Sopenharmony_ci return -EPERM; 7518c2ecf20Sopenharmony_ci err = br2684_setfilt(atmvcc, argp); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci return err; 7548c2ecf20Sopenharmony_ci#endif /* CONFIG_ATM_BR2684_IPFILTER */ 7558c2ecf20Sopenharmony_ci } 7568c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 7578c2ecf20Sopenharmony_ci} 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_cistatic struct atm_ioctl br2684_ioctl_ops = { 7608c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 7618c2ecf20Sopenharmony_ci .ioctl = br2684_ioctl, 7628c2ecf20Sopenharmony_ci}; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 7658c2ecf20Sopenharmony_cistatic void *br2684_seq_start(struct seq_file *seq, loff_t * pos) 7668c2ecf20Sopenharmony_ci __acquires(devs_lock) 7678c2ecf20Sopenharmony_ci{ 7688c2ecf20Sopenharmony_ci read_lock(&devs_lock); 7698c2ecf20Sopenharmony_ci return seq_list_start(&br2684_devs, *pos); 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_cistatic void *br2684_seq_next(struct seq_file *seq, void *v, loff_t * pos) 7738c2ecf20Sopenharmony_ci{ 7748c2ecf20Sopenharmony_ci return seq_list_next(v, &br2684_devs, pos); 7758c2ecf20Sopenharmony_ci} 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_cistatic void br2684_seq_stop(struct seq_file *seq, void *v) 7788c2ecf20Sopenharmony_ci __releases(devs_lock) 7798c2ecf20Sopenharmony_ci{ 7808c2ecf20Sopenharmony_ci read_unlock(&devs_lock); 7818c2ecf20Sopenharmony_ci} 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_cistatic int br2684_seq_show(struct seq_file *seq, void *v) 7848c2ecf20Sopenharmony_ci{ 7858c2ecf20Sopenharmony_ci const struct br2684_dev *brdev = list_entry(v, struct br2684_dev, 7868c2ecf20Sopenharmony_ci br2684_devs); 7878c2ecf20Sopenharmony_ci const struct net_device *net_dev = brdev->net_dev; 7888c2ecf20Sopenharmony_ci const struct br2684_vcc *brvcc; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci seq_printf(seq, "dev %.16s: num=%d, mac=%pM (%s)\n", 7918c2ecf20Sopenharmony_ci net_dev->name, 7928c2ecf20Sopenharmony_ci brdev->number, 7938c2ecf20Sopenharmony_ci net_dev->dev_addr, 7948c2ecf20Sopenharmony_ci brdev->mac_was_set ? "set" : "auto"); 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci list_for_each_entry(brvcc, &brdev->brvccs, brvccs) { 7978c2ecf20Sopenharmony_ci seq_printf(seq, " vcc %d.%d.%d: encaps=%s payload=%s" 7988c2ecf20Sopenharmony_ci ", failed copies %u/%u" 7998c2ecf20Sopenharmony_ci "\n", brvcc->atmvcc->dev->number, 8008c2ecf20Sopenharmony_ci brvcc->atmvcc->vpi, brvcc->atmvcc->vci, 8018c2ecf20Sopenharmony_ci (brvcc->encaps == e_llc) ? "LLC" : "VC", 8028c2ecf20Sopenharmony_ci (brdev->payload == p_bridged) ? "bridged" : "routed", 8038c2ecf20Sopenharmony_ci brvcc->copies_failed, brvcc->copies_needed); 8048c2ecf20Sopenharmony_ci#ifdef CONFIG_ATM_BR2684_IPFILTER 8058c2ecf20Sopenharmony_ci if (brvcc->filter.netmask != 0) 8068c2ecf20Sopenharmony_ci seq_printf(seq, " filter=%pI4/%pI4\n", 8078c2ecf20Sopenharmony_ci &brvcc->filter.prefix, 8088c2ecf20Sopenharmony_ci &brvcc->filter.netmask); 8098c2ecf20Sopenharmony_ci#endif /* CONFIG_ATM_BR2684_IPFILTER */ 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci return 0; 8128c2ecf20Sopenharmony_ci} 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_cistatic const struct seq_operations br2684_seq_ops = { 8158c2ecf20Sopenharmony_ci .start = br2684_seq_start, 8168c2ecf20Sopenharmony_ci .next = br2684_seq_next, 8178c2ecf20Sopenharmony_ci .stop = br2684_seq_stop, 8188c2ecf20Sopenharmony_ci .show = br2684_seq_show, 8198c2ecf20Sopenharmony_ci}; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ciextern struct proc_dir_entry *atm_proc_root; /* from proc.c */ 8228c2ecf20Sopenharmony_ci#endif /* CONFIG_PROC_FS */ 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_cistatic int __init br2684_init(void) 8258c2ecf20Sopenharmony_ci{ 8268c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 8278c2ecf20Sopenharmony_ci struct proc_dir_entry *p; 8288c2ecf20Sopenharmony_ci p = proc_create_seq("br2684", 0, atm_proc_root, &br2684_seq_ops); 8298c2ecf20Sopenharmony_ci if (p == NULL) 8308c2ecf20Sopenharmony_ci return -ENOMEM; 8318c2ecf20Sopenharmony_ci#endif 8328c2ecf20Sopenharmony_ci register_atm_ioctl(&br2684_ioctl_ops); 8338c2ecf20Sopenharmony_ci register_atmdevice_notifier(&atm_dev_notifier); 8348c2ecf20Sopenharmony_ci return 0; 8358c2ecf20Sopenharmony_ci} 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_cistatic void __exit br2684_exit(void) 8388c2ecf20Sopenharmony_ci{ 8398c2ecf20Sopenharmony_ci struct net_device *net_dev; 8408c2ecf20Sopenharmony_ci struct br2684_dev *brdev; 8418c2ecf20Sopenharmony_ci struct br2684_vcc *brvcc; 8428c2ecf20Sopenharmony_ci deregister_atm_ioctl(&br2684_ioctl_ops); 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 8458c2ecf20Sopenharmony_ci remove_proc_entry("br2684", atm_proc_root); 8468c2ecf20Sopenharmony_ci#endif 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci unregister_atmdevice_notifier(&atm_dev_notifier); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci while (!list_empty(&br2684_devs)) { 8528c2ecf20Sopenharmony_ci net_dev = list_entry_brdev(br2684_devs.next); 8538c2ecf20Sopenharmony_ci brdev = BRPRIV(net_dev); 8548c2ecf20Sopenharmony_ci while (!list_empty(&brdev->brvccs)) { 8558c2ecf20Sopenharmony_ci brvcc = list_entry_brvcc(brdev->brvccs.next); 8568c2ecf20Sopenharmony_ci br2684_close_vcc(brvcc); 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci list_del(&brdev->br2684_devs); 8608c2ecf20Sopenharmony_ci unregister_netdev(net_dev); 8618c2ecf20Sopenharmony_ci free_netdev(net_dev); 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci} 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_cimodule_init(br2684_init); 8668c2ecf20Sopenharmony_cimodule_exit(br2684_exit); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ciMODULE_AUTHOR("Marcell GAL"); 8698c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("RFC2684 bridged protocols over ATM/AAL5"); 8708c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 871