cregit-Linux how code gets into the kernel

Release 4.14 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/pkt_sched.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;
};

#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 lladdr[ETH_ALEN];
	
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%
Li RongQing12.27%125.00%
Martin Townsend12.27%125.00%
Alexander Aring12.27%125.00%
Total44100.00%4100.00%


static int iphc_decompress(struct sk_buff *skb, struct net_device *netdev, struct lowpan_peer *peer) { const u8 *saddr; saddr = peer->lladdr; return lowpan_header_decompress(skb, netdev, netdev->dev_addr, saddr); }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen3780.43%120.00%
Luiz Augusto von Dentz613.04%240.00%
Martin Townsend24.35%120.00%
Alexander Aring12.17%120.00%
Total46100.00%5100.00%


static int recv_pkt(struct sk_buff *skb, struct net_device *dev, struct lowpan_peer *peer) { 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, peer); if (ret < 0) { BT_DBG("iphc_decompress failed: %d", ret); 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 { BT_DBG("unknown packet type"); goto drop; } return NET_RX_SUCCESS; drop: dev->stats.rx_dropped++; return NET_RX_DROP; }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen23062.67%215.38%
Martin Townsend7119.35%646.15%
Alexander Aring318.45%17.69%
Luiz Augusto von Dentz154.09%215.38%
Glenn Ruben Bakke123.27%17.69%
Lukasz Duda82.18%17.69%
Total367100.00%13100.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, peer); if (err) { BT_DBG("recv pkt %d", err); err = -EAGAIN; } return err; }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen10096.15%240.00%
Johan Hedberg21.92%120.00%
Alexander Aring10.96%120.00%
Luiz Augusto von Dentz10.96%120.00%
Total104100.00%5100.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; u8 *daddr; 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; daddr = NULL; } else { BT_DBG("dest IP %pI6c", &ipv6_daddr); /* 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"); return -ENOENT; } daddr = peer->lladdr; *peer_addr = peer->chan->dst; *peer_addr_type = peer->chan->dst_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 Rissanen19383.91%440.00%
Luiz Augusto von Dentz187.83%220.00%
Glenn Ruben Bakke146.09%110.00%
Wei Yongjun20.87%110.00%
Alexander Aring20.87%110.00%
Colin Ian King10.43%110.00%
Total230100.00%10100.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%3100.00%
Total47100.00%3100.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 < 0) netdev->stats.tx_errors++; return err; }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen11780.69%250.00%
Al Viro2819.31%250.00%
Total145100.00%4100.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%480.00%
Alexander Aring31.89%120.00%
Total159100.00%5100.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%685.71%
Alexander Aring21.08%114.29%
Total185100.00%7100.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_MULTICAST; dev->watchdog_timeo = 0; dev->tx_queue_len = DEFAULT_TX_QUEUE_LEN; dev->netdev_ops = &netdev_ops; dev->header_ops = &header_ops; dev->needs_free_netdev = true; }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen5587.30%250.00%
Luiz Augusto von Dentz69.52%125.00%
David S. Miller23.17%125.00%
Total63100.00%4100.00%

static struct device_type bt_type = { .name = "bluetooth", };
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%2100.00%
Total44100.00%2100.00%


static void ifdown(struct net_device *netdev) { rtnl_lock(); dev_close(netdev); rtnl_unlock(); }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen22100.00%1100.00%
Total22100.00%1100.00%


static void do_notify_peers(struct work_struct *work) { struct lowpan_btle_dev *dev = container_of(work, struct lowpan_btle_dev, notify_peers.work); netdev_notify_peers(dev->netdev); /* send neighbour adv at startup */ }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen3494.44%266.67%
Alexander Aring25.56%133.33%
Total36100.00%3100.00%


static bool is_bt_6lowpan(struct hci_conn *hcon) { if (hcon->type != LE_LINK) return false; if (!enable_6lowpan) return false; return true; }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen33100.00%3100.00%
Total33100.00%3100.00%


static struct l2cap_chan *chan_create(void) { struct l2cap_chan *chan; chan = l2cap_chan_create(); if (!chan) return NULL; l2cap_chan_set_defaults(chan); chan->chan_type = L2CAP_CHAN_CONN_ORIENTED; chan->mode = L2CAP_MODE_LE_FLOWCTL; chan->imtu = 1280; return chan; }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen5398.15%375.00%
Johan Hedberg11.85%125.00%
Total54100.00%4100.00%


static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan, struct lowpan_btle_dev *dev, bool new_netdev) { struct lowpan_peer *peer; peer = kzalloc(sizeof(*peer), GFP_ATOMIC); if (!peer) return NULL; peer->chan = chan; memset(&peer->peer_addr, 0, sizeof(struct in6_addr)); baswap((void *)peer->lladdr, &chan->dst); lowpan_iphc_uncompress_eui48_lladdr(&peer->peer_addr, peer->lladdr); spin_lock(&devices_lock); INIT_LIST_HEAD(&peer->list); peer_add(dev, peer); spin_unlock(&devices_lock); /* Notifying peers about us needs to be done without locks held */ if (new_netdev) INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers); schedule_delayed_work(&dev->notify_peers, msecs_to_jiffies(100)); return peer->chan; }

Contributors

PersonTokensPropCommitsCommitProp
Jukka Rissanen14290.45%350.00%
Luiz Augusto von Dentz74.46%116.67%
Michael Scott74.46%116.67%
Alexander Aring10.64%116.67%
Total157100.00%6100.00%


static int setup_netdev(struct l2cap_chan *chan, struct lowpan_btle_dev **dev) { struct net_device *netdev; int err = 0; netdev = alloc_netdev(LOWPAN_PRIV_SIZE(sizeof(struct lowpan_btle_dev)), IFACE_NAME_TEMPLATE, NET_NAME_UNKNOWN, netdev_setup); if (!netdev) return -ENOMEM; netdev->addr_assign_type = NET_ADDR_PERM; baswap((void *)netdev->dev_addr, &chan->src); netdev->netdev_ops = &netdev_ops; SET_NETDEV_DEV(netdev, &chan->conn->hcon->hdev->dev); SET_NETDEV_DEVTYPE(netdev, &bt_type); *dev = lowpan_btle_dev(netdev); (*dev)->netdev = netdev; (*dev)->hdev = chan->conn->hcon->hdev; INIT_LIST_HEAD(&(*dev)->peers); spin_lock(&devices_lock); INIT_LIST_HEAD(&(*dev)->list); list_add_rcu(&(*dev)->list, &