diff options
author | Rebecca Schultz Zavin <rebecca@android.com> | 2011-04-22 15:54:34 -0700 |
---|---|---|
committer | Rebecca Schultz Zavin <rebecca@android.com> | 2011-04-22 15:54:34 -0700 |
commit | 2e35e1d7b965893e68f2fb1af77129406be5ff05 (patch) | |
tree | 4133e079309e275c0aaf96461bdda21d44c59038 /drivers/net | |
parent | 882113dbcbe92bca8fe08f67791e9b18d38a3fd3 (diff) | |
parent | 438f97f7bd3127a95174ee8c36c6826bd9ba11ea (diff) |
Merge remote branch 'common/android-2.6.36' into android-tegra-2.6.36
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/pppolac.c | 95 | ||||
-rw-r--r-- | drivers/net/pppopns.c | 87 | ||||
-rw-r--r-- | drivers/net/wireless/bcm4329/dhd.h | 5 | ||||
-rw-r--r-- | drivers/net/wireless/bcm4329/dhd_common.c | 78 | ||||
-rw-r--r-- | drivers/net/wireless/bcm4329/dhd_linux.c | 55 |
5 files changed, 298 insertions, 22 deletions
diff --git a/drivers/net/pppolac.c b/drivers/net/pppolac.c index af3202a920a0..c94b8507d92b 100644 --- a/drivers/net/pppolac.c +++ b/drivers/net/pppolac.c @@ -15,12 +15,15 @@ */ /* This driver handles L2TP data packets between a UDP socket and a PPP channel. - * To keep things simple, only one session per socket is permitted. Packets are - * sent via the socket, so it must keep connected to the same address. One must - * not set sequencing in ICCN but let LNS controll it. Currently this driver - * only works on IPv4 due to the lack of UDP encapsulation support in IPv6. */ + * The socket must keep connected, and only one session per socket is permitted. + * Sequencing of outgoing packets is controlled by LNS. Incoming packets with + * sequences are reordered within a sliding window of one second. Currently + * reordering only happens when a packet is received. It is done for simplicity + * since no additional locks or threads are required. This driver only works on + * IPv4 due to the lack of UDP encapsulation support in IPv6. */ #include <linux/module.h> +#include <linux/jiffies.h> #include <linux/workqueue.h> #include <linux/skbuff.h> #include <linux/file.h> @@ -53,14 +56,28 @@ static inline union unaligned *unaligned(void *ptr) return (union unaligned *)ptr; } +struct meta { + __u32 sequence; + __u32 timestamp; +}; + +static inline struct meta *skb_meta(struct sk_buff *skb) +{ + return (struct meta *)skb->cb; +} + +/******************************************************************************/ + static int pppolac_recv_core(struct sock *sk_udp, struct sk_buff *skb) { struct sock *sk = (struct sock *)sk_udp->sk_user_data; struct pppolac_opt *opt = &pppox_sk(sk)->proto.lac; + struct meta *meta = skb_meta(skb); + __u32 now = jiffies; __u8 bits; __u8 *ptr; - /* Drop the packet if it is too short. */ + /* Drop the packet if L2TP header is missing. */ if (skb->len < sizeof(struct udphdr) + 6) goto drop; @@ -99,9 +116,12 @@ static int pppolac_recv_core(struct sock *sk_udp, struct sk_buff *skb) if (unaligned(ptr)->u32 != opt->local) goto drop; - /* Check the sequence if it is present. According to RFC 2661 section - * 5.4, the only thing to do is to update opt->sequencing. */ - opt->sequencing = bits & L2TP_SEQUENCE_BIT; + /* Check the sequence if it is present. */ + if (bits & L2TP_SEQUENCE_BIT) { + meta->sequence = ptr[4] << 8 | ptr[5]; + if ((__s16)(meta->sequence - opt->recv_sequence) < 0) + goto drop; + } /* Skip PPP address and control if they are present. */ if (skb->len >= 2 && skb->data[0] == PPP_ADDR && @@ -112,7 +132,54 @@ static int pppolac_recv_core(struct sock *sk_udp, struct sk_buff *skb) if (skb->len >= 1 && skb->data[0] & 1) skb_push(skb, 1)[0] = 0; - /* Finally, deliver the packet to PPP channel. */ + /* Drop the packet if PPP protocol is missing. */ + if (skb->len < 2) + goto drop; + + /* Perform reordering if sequencing is enabled. */ + atomic_set(&opt->sequencing, bits & L2TP_SEQUENCE_BIT); + if (bits & L2TP_SEQUENCE_BIT) { + struct sk_buff *skb1; + + /* Insert the packet into receive queue in order. */ + skb_set_owner_r(skb, sk); + skb_queue_walk(&sk->sk_receive_queue, skb1) { + struct meta *meta1 = skb_meta(skb1); + __s16 order = meta->sequence - meta1->sequence; + if (order == 0) + goto drop; + if (order < 0) { + meta->timestamp = meta1->timestamp; + skb_insert(skb1, skb, &sk->sk_receive_queue); + skb = NULL; + break; + } + } + if (skb) { + meta->timestamp = now; + skb_queue_tail(&sk->sk_receive_queue, skb); + } + + /* Remove packets from receive queue as long as + * 1. the receive buffer is full, + * 2. they are queued longer than one second, or + * 3. there are no missing packets before them. */ + skb_queue_walk_safe(&sk->sk_receive_queue, skb, skb1) { + meta = skb_meta(skb); + if (atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf && + now - meta->timestamp < HZ && + meta->sequence != opt->recv_sequence) + break; + skb_unlink(skb, &sk->sk_receive_queue); + opt->recv_sequence = (__u16)(meta->sequence + 1); + skb_orphan(skb); + ppp_input(&pppox_sk(sk)->chan, skb); + } + return NET_RX_SUCCESS; + } + + /* Flush receive queue if sequencing is disabled. */ + skb_queue_purge(&sk->sk_receive_queue); skb_orphan(skb); ppp_input(&pppox_sk(sk)->chan, skb); return NET_RX_SUCCESS; @@ -163,14 +230,14 @@ static int pppolac_xmit(struct ppp_channel *chan, struct sk_buff *skb) skb->data[1] = PPP_CTRL; /* Install L2TP header. */ - if (opt->sequencing) { + if (atomic_read(&opt->sequencing)) { skb_push(skb, 10); skb->data[0] = L2TP_SEQUENCE_BIT; - skb->data[6] = opt->sequence >> 8; - skb->data[7] = opt->sequence; + skb->data[6] = opt->xmit_sequence >> 8; + skb->data[7] = opt->xmit_sequence; skb->data[8] = 0; skb->data[9] = 0; - opt->sequence++; + opt->xmit_sequence++; } else { skb_push(skb, 6); skb->data[0] = 0; @@ -246,6 +313,7 @@ static int pppolac_connect(struct socket *sock, struct sockaddr *useraddr, po->chan.mtu = PPP_MTU - 80; po->proto.lac.local = unaligned(&addr->local)->u32; po->proto.lac.remote = unaligned(&addr->remote)->u32; + atomic_set(&po->proto.lac.sequencing, 1); po->proto.lac.backlog_rcv = sk_udp->sk_backlog_rcv; error = ppp_register_channel(&po->chan); @@ -283,6 +351,7 @@ static int pppolac_release(struct socket *sock) if (sk->sk_state != PPPOX_NONE) { struct sock *sk_udp = (struct sock *)pppox_sk(sk)->chan.private; lock_sock(sk_udp); + skb_queue_purge(&sk->sk_receive_queue); pppox_unbind_sock(sk); udp_sk(sk_udp)->encap_type = 0; udp_sk(sk_udp)->encap_rcv = NULL; diff --git a/drivers/net/pppopns.c b/drivers/net/pppopns.c index 298097127c90..fb8198447938 100644 --- a/drivers/net/pppopns.c +++ b/drivers/net/pppopns.c @@ -16,11 +16,14 @@ /* This driver handles PPTP data packets between a RAW socket and a PPP channel. * The socket is created in the kernel space and connected to the same address - * of the control socket. To keep things simple, packets are always sent with - * sequence but without acknowledgement. This driver should work on both IPv4 - * and IPv6. */ + * of the control socket. Outgoing packets are always sent with sequences but + * without acknowledgements. Incoming packets with sequences are reordered + * within a sliding window of one second. Currently reordering only happens when + * a packet is received. It is done for simplicity since no additional locks or + * threads are required. This driver should work on both IPv4 and IPv6. */ #include <linux/module.h> +#include <linux/jiffies.h> #include <linux/workqueue.h> #include <linux/skbuff.h> #include <linux/file.h> @@ -52,21 +55,35 @@ struct header { __u32 sequence; } __attribute__((packed)); +struct meta { + __u32 sequence; + __u32 timestamp; +}; + +static inline struct meta *skb_meta(struct sk_buff *skb) +{ + return (struct meta *)skb->cb; +} + +/******************************************************************************/ + static int pppopns_recv_core(struct sock *sk_raw, struct sk_buff *skb) { struct sock *sk = (struct sock *)sk_raw->sk_user_data; struct pppopns_opt *opt = &pppox_sk(sk)->proto.pns; + struct meta *meta = skb_meta(skb); + __u32 now = jiffies; struct header *hdr; /* Skip transport header */ skb_pull(skb, skb_transport_header(skb) - skb->data); - /* Drop the packet if it is too short. */ + /* Drop the packet if GRE header is missing. */ if (skb->len < GRE_HEADER_SIZE) goto drop; + hdr = (struct header *)skb->data; /* Check the header. */ - hdr = (struct header *)skb->data; if (hdr->type != PPTP_GRE_TYPE || hdr->call != opt->local || (hdr->bits & PPTP_GRE_BITS_MASK) != PPTP_GRE_BITS) goto drop; @@ -81,6 +98,13 @@ static int pppopns_recv_core(struct sock *sk_raw, struct sk_buff *skb) if (skb->len != ntohs(hdr->length)) goto drop; + /* Check the sequence if it is present. */ + if (hdr->bits & PPTP_GRE_SEQ_BIT) { + meta->sequence = ntohl(hdr->sequence); + if ((__s32)(meta->sequence - opt->recv_sequence) < 0) + goto drop; + } + /* Skip PPP address and control if they are present. */ if (skb->len >= 2 && skb->data[0] == PPP_ADDR && skb->data[1] == PPP_CTRL) @@ -90,7 +114,53 @@ static int pppopns_recv_core(struct sock *sk_raw, struct sk_buff *skb) if (skb->len >= 1 && skb->data[0] & 1) skb_push(skb, 1)[0] = 0; - /* Finally, deliver the packet to PPP channel. */ + /* Drop the packet if PPP protocol is missing. */ + if (skb->len < 2) + goto drop; + + /* Perform reordering if sequencing is enabled. */ + if (hdr->bits & PPTP_GRE_SEQ_BIT) { + struct sk_buff *skb1; + + /* Insert the packet into receive queue in order. */ + skb_set_owner_r(skb, sk); + skb_queue_walk(&sk->sk_receive_queue, skb1) { + struct meta *meta1 = skb_meta(skb1); + __s32 order = meta->sequence - meta1->sequence; + if (order == 0) + goto drop; + if (order < 0) { + meta->timestamp = meta1->timestamp; + skb_insert(skb1, skb, &sk->sk_receive_queue); + skb = NULL; + break; + } + } + if (skb) { + meta->timestamp = now; + skb_queue_tail(&sk->sk_receive_queue, skb); + } + + /* Remove packets from receive queue as long as + * 1. the receive buffer is full, + * 2. they are queued longer than one second, or + * 3. there are no missing packets before them. */ + skb_queue_walk_safe(&sk->sk_receive_queue, skb, skb1) { + meta = skb_meta(skb); + if (atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf && + now - meta->timestamp < HZ && + meta->sequence != opt->recv_sequence) + break; + skb_unlink(skb, &sk->sk_receive_queue); + opt->recv_sequence = meta->sequence + 1; + skb_orphan(skb); + ppp_input(&pppox_sk(sk)->chan, skb); + } + return NET_RX_SUCCESS; + } + + /* Flush receive queue if sequencing is disabled. */ + skb_queue_purge(&sk->sk_receive_queue); skb_orphan(skb); ppp_input(&pppox_sk(sk)->chan, skb); return NET_RX_SUCCESS; @@ -151,8 +221,8 @@ static int pppopns_xmit(struct ppp_channel *chan, struct sk_buff *skb) hdr->type = PPTP_GRE_TYPE; hdr->length = htons(length); hdr->call = opt->remote; - hdr->sequence = htonl(opt->sequence); - opt->sequence++; + hdr->sequence = htonl(opt->xmit_sequence); + opt->xmit_sequence++; /* Now send the packet via the delivery queue. */ skb_set_owner_w(skb, sk_raw); @@ -261,6 +331,7 @@ static int pppopns_release(struct socket *sock) if (sk->sk_state != PPPOX_NONE) { struct sock *sk_raw = (struct sock *)pppox_sk(sk)->chan.private; lock_sock(sk_raw); + skb_queue_purge(&sk->sk_receive_queue); pppox_unbind_sock(sk); sk_raw->sk_data_ready = pppox_sk(sk)->proto.pns.data_ready; sk_raw->sk_backlog_rcv = pppox_sk(sk)->proto.pns.backlog_rcv; diff --git a/drivers/net/wireless/bcm4329/dhd.h b/drivers/net/wireless/bcm4329/dhd.h index 6c2998806e4d..7b1d076763ef 100644 --- a/drivers/net/wireless/bcm4329/dhd.h +++ b/drivers/net/wireless/bcm4329/dhd.h @@ -448,4 +448,9 @@ extern char nv_path[MOD_PARAM_PATHLEN]; extern void dhd_wait_for_event(dhd_pub_t *dhd, bool *lockvar); extern void dhd_wait_event_wakeup(dhd_pub_t*dhd); +/* dhd_commn arp offload wrapers */ +extern void dhd_arp_cleanup(dhd_pub_t *dhd); +int dhd_arp_get_arp_hostip_table(dhd_pub_t *dhd, void *buf, int buflen); +void dhd_arp_offload_add_ip(dhd_pub_t *dhd, u32 ipaddr); + #endif /* _dhd_h_ */ diff --git a/drivers/net/wireless/bcm4329/dhd_common.c b/drivers/net/wireless/bcm4329/dhd_common.c index 8fcb95fde827..e50da1414c9e 100644 --- a/drivers/net/wireless/bcm4329/dhd_common.c +++ b/drivers/net/wireless/bcm4329/dhd_common.c @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhd_common.c,v 1.5.6.8.2.6.6.69.4.25 2011/02/11 21:16:02 Exp $ + * $Id: dhd_common.c,v 1.5.6.8.2.6.6.69.4.25 2011-02-11 21:16:02 Exp $ */ #include <typedefs.h> #include <osl.h> @@ -1220,6 +1220,82 @@ dhd_arp_offload_enable(dhd_pub_t * dhd, int arp_enable) } #endif + +void dhd_arp_cleanup(dhd_pub_t *dhd) +{ +#ifdef ARP_OFFLOAD_SUPPORT + int ret = 0; + int iov_len = 0; + char iovbuf[128]; + + if (dhd == NULL) return; + + dhd_os_proto_block(dhd); + + iov_len = bcm_mkiovar("arp_hostip_clear", 0, 0, iovbuf, sizeof(iovbuf)); + if ((ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, iov_len)) < 0) + DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret)); + + iov_len = bcm_mkiovar("arp_table_clear", 0, 0, iovbuf, sizeof(iovbuf)); + if ((ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, iov_len)) < 0) + DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret)); + + dhd_os_proto_unblock(dhd); + +#endif /* ARP_OFFLOAD_SUPPORT */ +} + +void dhd_arp_offload_add_ip(dhd_pub_t *dhd, u32 ipaddr) +{ +#ifdef ARP_OFFLOAD_SUPPORT + int iov_len = 0; + char iovbuf[32]; + int retcode; + + dhd_os_proto_block(dhd); + + iov_len = bcm_mkiovar("arp_hostip", (char *)&ipaddr, 4, iovbuf, sizeof(iovbuf)); + retcode = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, iov_len); + + dhd_os_proto_unblock(dhd); + + if (retcode) + DHD_TRACE(("%s: ARP ip addr add failed, retcode = %d\n", + __FUNCTION__, retcode)); + else + DHD_TRACE(("%s: ARP ipaddr entry added\n", + __FUNCTION__)); +#endif /* ARP_OFFLOAD_SUPPORT */ +} + + +int dhd_arp_get_arp_hostip_table(dhd_pub_t *dhd, void *buf, int buflen) +{ +#ifdef ARP_OFFLOAD_SUPPORT + int retcode; + int iov_len = 0; + + if (!buf) + return -1; + + dhd_os_proto_block(dhd); + + iov_len = bcm_mkiovar("arp_hostip", 0, 0, buf, buflen); + retcode = dhdcdc_query_ioctl(dhd, 0, WLC_GET_VAR, buf, buflen); + + dhd_os_proto_unblock(dhd); + + if (retcode) { + DHD_TRACE(("%s: ioctl WLC_GET_VAR error %d\n", + __FUNCTION__, retcode)); + + return -1; + } +#endif /* ARP_OFFLOAD_SUPPORT */ + return 0; +} + + int dhd_preinit_ioctls(dhd_pub_t *dhd) { diff --git a/drivers/net/wireless/bcm4329/dhd_linux.c b/drivers/net/wireless/bcm4329/dhd_linux.c index 3bfdd6989fba..bbb15b93c013 100644 --- a/drivers/net/wireless/bcm4329/dhd_linux.c +++ b/drivers/net/wireless/bcm4329/dhd_linux.c @@ -43,6 +43,7 @@ #include <linux/ethtool.h> #include <linux/fcntl.h> #include <linux/fs.h> +#include <linux/inetdevice.h> #include <asm/uaccess.h> #include <asm/unaligned.h> @@ -201,6 +202,12 @@ void wifi_del_dev(void) } #endif /* defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC) */ +static int dhd_device_event(struct notifier_block *this, unsigned long event, + void *ptr); + +static struct notifier_block dhd_notifier = { + .notifier_call = dhd_device_event +}; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) #include <linux/suspend.h> @@ -2015,6 +2022,7 @@ dhd_del_if(dhd_info_t *dhd, int ifidx) up(&dhd->sysioc_sem); } + dhd_pub_t * dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) { @@ -2171,6 +2179,8 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) register_early_suspend(&dhd->early_suspend); #endif + register_inetaddr_notifier(&dhd_notifier); + return &dhd->pub; fail: @@ -2331,6 +2341,48 @@ static struct net_device_ops dhd_ops_virt = { }; #endif +static int dhd_device_event(struct notifier_block *this, unsigned long event, + void *ptr) +{ + struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; + dhd_info_t *dhd; + dhd_pub_t *dhd_pub; + + if (!ifa) + return NOTIFY_DONE; + + dhd = *(dhd_info_t **)netdev_priv(ifa->ifa_dev->dev); + dhd_pub = &dhd->pub; + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 31)) + if (ifa->ifa_dev->dev->netdev_ops == &dhd_ops_pri) { +#else + if (ifa->ifa_dev->dev->open == &dhd_open) { +#endif + switch (event) { + case NETDEV_UP: + DHD_TRACE(("%s: [%s] Up IP: 0x%x\n", + __FUNCTION__, ifa->ifa_label, ifa->ifa_address)); + + dhd_arp_cleanup(dhd_pub); + break; + + case NETDEV_DOWN: + DHD_TRACE(("%s: [%s] Down IP: 0x%x\n", + __FUNCTION__, ifa->ifa_label, ifa->ifa_address)); + + dhd_arp_cleanup(dhd_pub); + break; + + default: + DHD_TRACE(("%s: [%s] Event: %lu\n", + __FUNCTION__, ifa->ifa_label, event)); + break; + } + } + return NOTIFY_DONE; +} + int dhd_net_attach(dhd_pub_t *dhdp, int ifidx) { @@ -2404,6 +2456,7 @@ dhd_net_attach(dhd_pub_t *dhdp, int ifidx) dhd->pub.mac.octet[0], dhd->pub.mac.octet[1], dhd->pub.mac.octet[2], dhd->pub.mac.octet[3], dhd->pub.mac.octet[4], dhd->pub.mac.octet[5]); + #if defined(CONFIG_WIRELESS_EXT) #if defined(CONFIG_FIRST_SCAN) #ifdef SOFTAP @@ -2469,6 +2522,8 @@ dhd_detach(dhd_pub_t *dhdp) dhd_if_t *ifp; int i; + unregister_inetaddr_notifier(&dhd_notifier); + #if defined(CONFIG_HAS_EARLYSUSPEND) if (dhd->early_suspend.suspend) unregister_early_suspend(&dhd->early_suspend); |