cregit-Linux how code gets into the kernel

Release 4.11 net/bluetooth/6lowpan.c

Directory: net/bluetooth
/*
   Copyright (c) 2013-2014 Intel Corp.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License version 2 and
   only version 2 as published by the Free Software Foundation.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
*/

#include <linux/if_arp.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/module.h>
#include <linux/debugfs.h>

#include <net/ipv6.h>
#include <net/ip6_route.h>
#include <net/addrconf.h>

#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>

#include <net/6lowpan.h> /* for the compression support */


#define VERSION "0.1"


static struct dentry *lowpan_enable_debugfs;

static struct dentry *lowpan_control_debugfs;


#define IFACE_NAME_TEMPLATE "bt%d"


struct skb_cb {
	
struct in6_addr addr;
	
struct in6_addr gw;
	
struct l2cap_chan *chan;
	
int status;
};

#define lowpan_cb(skb) ((struct skb_cb *)((skb)->cb))

/* The devices list contains those devices that we are acting
 * as a proxy. The BT 6LoWPAN device is a virtual device that
 * connects to the Bluetooth LE device. The real connection to
 * BT device is done via l2cap layer. There exists one
 * virtual device / one BT 6LoWPAN network (=hciX device).
 * The list contains struct lowpan_dev elements.
 */
static LIST_HEAD(bt_6lowpan_devices);
static DEFINE_SPINLOCK(devices_lock);


static bool enable_6lowpan;

/* We are listening incoming connections via this channel
 */

static struct l2cap_chan *listen_chan;


struct lowpan_peer {
	
struct list_head list;
	
struct rcu_head rcu;
	
struct l2cap_chan *chan;

	/* peer addresses in various formats */
	
unsigned char eui64_addr[EUI64_ADDR_LEN];
	
struct in6_addr peer_addr;
};


struct lowpan_btle_dev {
	
struct list_head list;

	
struct hci_dev *hdev;
	
struct net_device *netdev;
	
struct list_head peers;
	
atomic_t peer_count; /* number of items in peers list */

	
struct work_struct delete_netdev;
	
struct delayed_work notify_peers;
};


static inline struct lowpan_btle_dev * lowpan_btle_dev(const struct net_device *netdev) { return (struct lowpan_btle_dev *)lowpan_dev(netdev)->priv; }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen1864.29%133.33%
Alexander Aring1035.71%266.67%
Total28100.00%3100.00%


static inline void peer_add(struct lowpan_btle_dev *dev, struct lowpan_peer *peer) { list_add_rcu(&peer->list, &dev->peers); atomic_inc(&dev->peer_count); }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen3797.37%266.67%
Alexander Aring12.63%133.33%
Total38100.00%3100.00%


static inline bool peer_del(struct lowpan_btle_dev *dev, struct lowpan_peer *peer) { list_del_rcu(&peer->list); kfree_rcu(peer, rcu); module_put(THIS_MODULE); if (atomic_dec_and_test(&dev->peer_count)) { BT_DBG("last peer"); return true; } return false; }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen5795.00%360.00%
Johan Hedberg23.33%120.00%
Alexander Aring11.67%120.00%
Total60100.00%5100.00%


static inline struct lowpan_peer *peer_lookup_ba(struct lowpan_btle_dev *dev, bdaddr_t *ba, __u8 type) { struct lowpan_peer *peer; BT_DBG("peers %d addr %pMR type %d", atomic_read(&dev->peer_count), ba, type); rcu_read_lock(); list_for_each_entry_rcu(peer, &dev->peers, list) { BT_DBG("dst addr %pMR dst type %d", &peer->chan->dst, peer->chan->dst_type); if (bacmp(&peer->chan->dst, ba)) continue; if (type == peer->chan->dst_type) { rcu_read_unlock(); return peer; } } rcu_read_unlock(); return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen11299.12%375.00%
Alexander Aring10.88%125.00%
Total113100.00%4100.00%


static inline struct lowpan_peer * __peer_lookup_chan(struct lowpan_btle_dev *dev, struct l2cap_chan *chan) { struct lowpan_peer *peer; list_for_each_entry_rcu(peer, &dev->peers, list) { if (peer->chan == chan) return peer; } return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen4797.92%266.67%
Alexander Aring12.08%133.33%
Total48100.00%3100.00%


static inline struct lowpan_peer * __peer_lookup_conn(struct lowpan_btle_dev *dev, struct l2cap_conn *conn) { struct lowpan_peer *peer; list_for_each_entry_rcu(peer, &dev->peers, list) { if (peer->chan->conn == conn) return peer; } return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen4998.00%375.00%
Alexander Aring12.00%125.00%
Total50100.00%4100.00%


static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_btle_dev *dev, struct in6_addr *daddr, struct sk_buff *skb) { struct lowpan_peer *peer; struct in6_addr *nexthop; struct rt6_info *rt = (struct rt6_info *)skb_dst(skb); int count = atomic_read(&dev->peer_count); BT_DBG("peers %d addr %pI6c rt %p", count, daddr, rt); /* If we have multiple 6lowpan peers, then check where we should * send the packet. If only one peer exists, then we can send the * packet right away. */ if (count == 1) { rcu_read_lock(); peer = list_first_or_null_rcu(&dev->peers, struct lowpan_peer, list); rcu_read_unlock(); return peer; } if (!rt) { nexthop = &lowpan_cb(skb)->gw; if (ipv6_addr_any(nexthop)) return NULL; } else { nexthop = rt6_nexthop(rt, daddr); /* We need to remember the address because it is needed * by bt_xmit() when sending the packet. In bt_xmit(), the * destination routing info is not set. */ memcpy(&lowpan_cb(skb)->gw, nexthop, sizeof(struct in6_addr)); } BT_DBG("gw %pI6c", nexthop); rcu_read_lock(); list_for_each_entry_rcu(peer, &dev->peers, list) { BT_DBG("dst addr %pMR dst type %d ip %pI6c", &peer->chan->dst, peer->chan->dst_type, &peer->peer_addr); if (!ipv6_addr_cmp(&peer->peer_addr, nexthop)) { rcu_read_unlock(); return peer; } } rcu_read_unlock(); return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen23098.71%250.00%
Martin KaFai Lau20.86%125.00%
Alexander Aring10.43%125.00%
Total233100.00%4100.00%


static struct lowpan_peer *lookup_peer(struct l2cap_conn *conn) { struct lowpan_btle_dev *entry; struct lowpan_peer *peer = NULL; rcu_read_lock(); list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) { peer = __peer_lookup_conn(entry, conn); if (peer) break; } rcu_read_unlock(); return peer; }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen5798.28%266.67%
Alexander Aring11.72%133.33%
Total58100.00%3100.00%


static struct lowpan_btle_dev *lookup_dev(struct l2cap_conn *conn) { struct lowpan_btle_dev *entry; struct lowpan_btle_dev *dev = NULL; rcu_read_lock(); list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) { if (conn->hcon->hdev == entry->hdev) { dev = entry; break; } } rcu_read_unlock(); return dev; }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen6095.24%266.67%
Alexander Aring34.76%133.33%
Total63100.00%3100.00%


static int give_skb_to_upper(struct sk_buff *skb, struct net_device *dev) { struct sk_buff *skb_cp; skb_cp = skb_copy(skb, GFP_ATOMIC); if (!skb_cp) return NET_RX_DROP; return netif_rx_ni(skb_cp); }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen4193.18%125.00%
Martin Townsend12.27%125.00%
Alexander Aring12.27%125.00%
Li RongQing12.27%125.00%
Total44100.00%4100.00%


static int iphc_decompress(struct sk_buff *skb, struct net_device *netdev, struct l2cap_chan *chan) { const u8 *saddr, *daddr; struct lowpan_btle_dev *dev; struct lowpan_peer *peer; dev = lowpan_btle_dev(netdev); rcu_read_lock(); peer = __peer_lookup_chan(dev, chan); rcu_read_unlock(); if (!peer) return -EINVAL; saddr = peer->eui64_addr; daddr = dev->netdev->dev_addr; return lowpan_header_decompress(skb, netdev, daddr, saddr); }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen8790.62%342.86%
Martin Townsend66.25%228.57%
Alexander Aring33.12%228.57%
Total96100.00%7100.00%


static int recv_pkt(struct sk_buff *skb, struct net_device *dev, struct l2cap_chan *chan) { struct sk_buff *local_skb; int ret; if (!netif_running(dev)) goto drop; if (dev->type != ARPHRD_6LOWPAN || !skb->len) goto drop; skb_reset_network_header(skb); skb = skb_share_check(skb, GFP_ATOMIC); if (!skb) goto drop; /* check that it's our buffer */ if (lowpan_is_ipv6(*skb_network_header(skb))) { /* Pull off the 1-byte of 6lowpan header. */ skb_pull(skb, 1); /* Copy the packet so that the IPv6 header is * properly aligned. */ local_skb = skb_copy_expand(skb, NET_SKB_PAD - 1, skb_tailroom(skb), GFP_ATOMIC); if (!local_skb) goto drop; local_skb->protocol = htons(ETH_P_IPV6); local_skb->pkt_type = PACKET_HOST; local_skb->dev = dev; skb_set_transport_header(local_skb, sizeof(struct ipv6hdr)); if (give_skb_to_upper(local_skb, dev) != NET_RX_SUCCESS) { kfree_skb(local_skb); goto drop; } dev->stats.rx_bytes += skb->len; dev->stats.rx_packets++; consume_skb(local_skb); consume_skb(skb); } else if (lowpan_is_iphc(*skb_network_header(skb))) { local_skb = skb_clone(skb, GFP_ATOMIC); if (!local_skb) goto drop; local_skb->dev = dev; ret = iphc_decompress(local_skb, dev, chan); if (ret < 0) { kfree_skb(local_skb); goto drop; } local_skb->protocol = htons(ETH_P_IPV6); local_skb->pkt_type = PACKET_HOST; if (give_skb_to_upper(local_skb, dev) != NET_RX_SUCCESS) { kfree_skb(local_skb); goto drop; } dev->stats.rx_bytes += skb->len; dev->stats.rx_packets++; consume_skb(local_skb); consume_skb(skb); } else { goto drop; } return NET_RX_SUCCESS; drop: dev->stats.rx_dropped++; return NET_RX_DROP; }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen23365.63%218.18%
Martin Townsend7120.00%654.55%
Alexander Aring318.73%19.09%
Glenn Ruben Bakke123.38%19.09%
Lukasz Duda82.25%19.09%
Total355100.00%11100.00%

/* Packet from BT LE device */
static int chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) { struct lowpan_btle_dev *dev; struct lowpan_peer *peer; int err; peer = lookup_peer(chan->conn); if (!peer) return -ENOENT; dev = lookup_dev(chan->conn); if (!dev || !dev->netdev) return -ENOENT; err = recv_pkt(skb, dev->netdev, chan); if (err) { BT_DBG("recv pkt %d", err); err = -EAGAIN; } return err; }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen10197.12%250.00%
Johan Hedberg21.92%125.00%
Alexander Aring10.96%125.00%
Total104100.00%4100.00%


static u8 get_addr_type_from_eui64(u8 byte) { /* Is universal(0) or local(1) bit */ return ((byte & 0x02) ? BDADDR_LE_RANDOM : BDADDR_LE_PUBLIC); }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen23100.00%2100.00%
Total23100.00%2100.00%


static void copy_to_bdaddr(struct in6_addr *ip6_daddr, bdaddr_t *addr) { u8 *eui64 = ip6_daddr->s6_addr + 8; addr->b[0] = eui64[7]; addr->b[1] = eui64[6]; addr->b[2] = eui64[5]; addr->b[3] = eui64[2]; addr->b[4] = eui64[1]; addr->b[5] = eui64[0]; }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen97100.00%2100.00%
Total97100.00%2100.00%


static void convert_dest_bdaddr(struct in6_addr *ip6_daddr, bdaddr_t *addr, u8 *addr_type) { copy_to_bdaddr(ip6_daddr, addr); /* We need to toggle the U/L bit that we got from IPv6 address * so that we get the proper address and type of the BD address. */ addr->b[5] ^= 0x02; *addr_type = get_addr_type_from_eui64(addr->b[5]); }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen49100.00%2100.00%
Total49100.00%2100.00%


static int setup_header(struct sk_buff *skb, struct net_device *netdev, bdaddr_t *peer_addr, u8 *peer_addr_type) { struct in6_addr ipv6_daddr; struct ipv6hdr *hdr; struct lowpan_btle_dev *dev; struct lowpan_peer *peer; bdaddr_t addr, *any = BDADDR_ANY; u8 *daddr = any->b; int err, status = 0; hdr = ipv6_hdr(skb); dev = lowpan_btle_dev(netdev); memcpy(&ipv6_daddr, &hdr->daddr, sizeof(ipv6_daddr)); if (ipv6_addr_is_multicast(&ipv6_daddr)) { lowpan_cb(skb)->chan = NULL; } else { u8 addr_type; /* Get destination BT device from skb. * If there is no such peer then discard the packet. */ convert_dest_bdaddr(&ipv6_daddr, &addr, &addr_type); BT_DBG("dest addr %pMR type %d IP %pI6c", &addr, addr_type, &ipv6_daddr); peer = peer_lookup_ba(dev, &addr, addr_type); if (!peer) { /* The packet might be sent to 6lowpan interface * because of routing (either via default route * or user set route) so get peer according to * the destination address. */ peer = peer_lookup_dst(dev, &ipv6_daddr, skb); if (!peer) { BT_DBG("no such peer %pMR found", &addr); return -ENOENT; } } daddr = peer->eui64_addr; *peer_addr = addr; *peer_addr_type = addr_type; lowpan_cb(skb)->chan = peer->chan; status = 1; } lowpan_header_compress(skb, netdev, daddr, dev->netdev->dev_addr); err = dev_hard_header(skb, netdev, ETH_P_IPV6, NULL, NULL, 0); if (err < 0) return err; return status; }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen25693.77%457.14%
Glenn Ruben Bakke145.13%114.29%
Alexander Aring20.73%114.29%
Wei Yongjun10.37%114.29%
Total273100.00%7100.00%


static int header_create(struct sk_buff *skb, struct net_device *netdev, unsigned short type, const void *_daddr, const void *_saddr, unsigned int len) { if (type != ETH_P_IPV6) return -EINVAL; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen47100.00%2100.00%
Total47100.00%2100.00%

/* Packet to BT LE device */
static int send_pkt(struct l2cap_chan *chan, struct sk_buff *skb, struct net_device *netdev) { struct msghdr msg; struct kvec iv; int err; /* Remember the skb so that we can send EAGAIN to the caller if * we run out of credits. */ chan->data = skb; iv.iov_base = skb->data; iv.iov_len = skb->len; memset(&msg, 0, sizeof(msg)); iov_iter_kvec(&msg.msg_iter, WRITE | ITER_KVEC, &iv, 1, skb->len); err = l2cap_chan_send(chan, &msg, skb->len); if (err > 0) { netdev->stats.tx_bytes += err; netdev->stats.tx_packets++; return 0; } if (!err) err = lowpan_cb(skb)->status; if (err < 0) { if (err == -EAGAIN) netdev->stats.tx_dropped++; else netdev->stats.tx_errors++; } return err; }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen14884.09%466.67%
Al Viro2815.91%233.33%
Total176100.00%6100.00%


static int send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev) { struct sk_buff *local_skb; struct lowpan_btle_dev *entry; int err = 0; rcu_read_lock(); list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) { struct lowpan_peer *pentry; struct lowpan_btle_dev *dev; if (entry->netdev != netdev) continue; dev = lowpan_btle_dev(entry->netdev); list_for_each_entry_rcu(pentry, &dev->peers, list) { int ret; local_skb = skb_clone(skb, GFP_ATOMIC); BT_DBG("xmit %s to %pMR type %d IP %pI6c chan %p", netdev->name, &pentry->chan->dst, pentry->chan->dst_type, &pentry->peer_addr, pentry->chan); ret = send_pkt(pentry->chan, local_skb, netdev); if (ret < 0) err = ret; kfree_skb(local_skb); } } rcu_read_unlock(); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen15698.11%685.71%
Alexander Aring31.89%114.29%
Total159100.00%7100.00%


static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev) { int err = 0; bdaddr_t addr; u8 addr_type; /* We must take a copy of the skb before we modify/replace the ipv6 * header as the header could be used elsewhere */ skb = skb_unshare(skb, GFP_ATOMIC); if (!skb) return NET_XMIT_DROP; /* Return values from setup_header() * <0 - error, packet is dropped * 0 - this is a multicast packet * 1 - this is unicast packet */ err = setup_header(skb, netdev, &addr, &addr_type); if (err < 0) { kfree_skb(skb); return NET_XMIT_DROP; } if (err) { if (lowpan_cb(skb)->chan) { BT_DBG("xmit %s to %pMR type %d IP %pI6c chan %p", netdev->name, &addr, addr_type, &lowpan_cb(skb)->addr, lowpan_cb(skb)->chan); err = send_pkt(lowpan_cb(skb)->chan, skb, netdev); } else { err = -ENOENT; } } else { /* We need to send the packet to every device behind this * interface. */ err = send_mcast_pkt(skb, netdev); } dev_kfree_skb(skb); if (err) BT_DBG("ERROR: xmit failed (%d)", err); return err < 0 ? NET_XMIT_DROP : err; }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen18398.92%787.50%
Alexander Aring21.08%112.50%
Total185100.00%8100.00%


static int bt_dev_init(struct net_device *dev) { netdev_lockdep_set_classes(dev); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen1894.74%150.00%
Eric Dumazet15.26%150.00%
Total19100.00%2100.00%

static const struct net_device_ops netdev_ops = { .ndo_init = bt_dev_init, .ndo_start_xmit = bt_xmit, }; static struct header_ops header_ops = { .create = header_create, };
static void netdev_setup(struct net_device *dev) { dev->hard_header_len = 0; dev->needed_tailroom = 0; dev->flags = IFF_RUNNING | IFF_POINTOPOINT | IFF_MULTICAST; dev->watchdog_timeo = 0; dev->netdev_ops = &netdev_ops; dev->header_ops = &header_ops; dev->destructor = free_netdev; }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen59100.00%3100.00%
Total59100.00%3100.00%

static struct device_type bt_type = { .name = "bluetooth", };
static void set_addr(u8 *eui, u8 *addr, u8 addr_type) { /* addr is the BT address in little-endian format */ eui[0] = addr[5]; eui[1] = addr[4]; eui[2] = addr[3]; eui[3] = 0xFF; eui[4] = 0xFE; eui[5] = addr[2]; eui[6] = addr[1]; eui[7] = addr[0]; /* Universal/local bit set, BT 6lowpan draft ch. 3.2.1 */ if (addr_type == BDADDR_LE_PUBLIC) eui[0] &= ~0x02; else eui[0] |= 0x02; BT_DBG("type %d addr %*phC", addr_type, 8, eui); }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen126100.00%1100.00%
Total126100.00%1100.00%


static void set_dev_addr(struct net_device *netdev, bdaddr_t *addr, u8 addr_type) { netdev->addr_assign_type = NET_ADDR_PERM; set_addr(netdev->dev_addr, addr->b, addr_type); }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen37100.00%1100.00%
Total37100.00%1100.00%


static void ifup(struct net_device *netdev) { int err; rtnl_lock(); err = dev_open(netdev); if (err < 0) BT_INFO("iface %s cannot be opened (%d)", netdev->name, err); rtnl_unlock(); }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen44100.00%1100.00%
Total44100.00%1100.00%


static void ifdown(struct net_device *netdev) { int err; rtnl_lock(); err = dev_close(netdev); if (err < 0) BT_INFO("iface %s cannot be closed (%d)", netdev->name, err); rtnl_unlock(); }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen44100.00%1100.00%
Total44100.00%1100.00%


static void do_notify_peers(struct