diff options
author | Patrick McHardy <kaber@trash.net> | 2011-06-16 17:01:10 +0200 |
---|---|---|
committer | Patrick McHardy <kaber@trash.net> | 2011-06-16 17:01:10 +0200 |
commit | 1f2d9c9dd8276b1c19ef80e6d8059fe47506d4c3 (patch) | |
tree | c5a3439c1c140eb87f7e3ece2e1148dece037b4f /net | |
parent | a7fed7620b6eeaba98a558df46bb50a31828b462 (diff) | |
parent | 520b2756d9414cb6b0f6fc70714e95ee9248ebf4 (diff) |
Merge branch 'master' of /repos/git/net-next-2.6
Diffstat (limited to 'net')
360 files changed, 13035 insertions, 7545 deletions
diff --git a/net/802/garp.c b/net/802/garp.c index c1df2dad8c6..16102951d36 100644 --- a/net/802/garp.c +++ b/net/802/garp.c @@ -554,8 +554,7 @@ static void garp_release_port(struct net_device *dev) return; } rcu_assign_pointer(dev->garp_port, NULL); - synchronize_rcu(); - kfree(port); + kfree_rcu(port, rcu); } int garp_init_applicant(struct net_device *dev, struct garp_application *appl) @@ -607,7 +606,6 @@ void garp_uninit_applicant(struct net_device *dev, struct garp_application *appl ASSERT_RTNL(); rcu_assign_pointer(port->applicants[appl->type], NULL); - synchronize_rcu(); /* Delete timer and generate a final TRANSMIT_PDU event to flush out * all pending messages before the applicant is gone. */ @@ -617,7 +615,7 @@ void garp_uninit_applicant(struct net_device *dev, struct garp_application *appl garp_queue_xmit(app); dev_mc_del(dev, appl->proto.group_address); - kfree(app); + kfree_rcu(app, rcu); garp_release_port(dev); } EXPORT_SYMBOL_GPL(garp_uninit_applicant); diff --git a/net/802/psnap.c b/net/802/psnap.c index 21cde8fd579..db6baf7cf6e 100644 --- a/net/802/psnap.c +++ b/net/802/psnap.c @@ -147,7 +147,6 @@ struct datalink_proto *register_snap_client(const unsigned char *desc, out: spin_unlock_bh(&snap_lock); - synchronize_net(); return proto; } diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 969e7004cf8..cfa9afe9b11 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -18,6 +18,8 @@ * 2 of the License, or (at your option) any later version. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/capability.h> #include <linux/module.h> #include <linux/netdevice.h> @@ -46,8 +48,6 @@ int vlan_net_id __read_mostly; const char vlan_fullname[] = "802.1Q VLAN Support"; const char vlan_version[] = DRV_VERSION; -static const char vlan_copyright[] = "Ben Greear <greearb@candelatech.com>"; -static const char vlan_buggyright[] = "David S. Miller <davem@redhat.com>"; /* End of global variables definitions. */ @@ -119,10 +119,14 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head) grp->nr_vlans--; - vlan_group_set_device(grp, vlan_id, NULL); - if (!grp->killall) - synchronize_net(); + if (vlan->flags & VLAN_FLAG_GVRP) + vlan_gvrp_request_leave(dev); + vlan_group_set_device(grp, vlan_id, NULL); + /* Because unregister_netdevice_queue() makes sure at least one rcu + * grace period is respected before device freeing, + * we dont need to call synchronize_net() here. + */ unregister_netdevice_queue(dev, head); /* If the group is now empty, kill off the group. */ @@ -147,13 +151,13 @@ int vlan_check_real_dev(struct net_device *real_dev, u16 vlan_id) const struct net_device_ops *ops = real_dev->netdev_ops; if (real_dev->features & NETIF_F_VLAN_CHALLENGED) { - pr_info("8021q: VLANs not supported on %s\n", name); + pr_info("VLANs not supported on %s\n", name); return -EOPNOTSUPP; } if ((real_dev->features & NETIF_F_HW_VLAN_FILTER) && (!ops->ndo_vlan_rx_add_vid || !ops->ndo_vlan_rx_kill_vid)) { - pr_info("8021q: Device %s has buggy VLAN hw accel\n", name); + pr_info("Device %s has buggy VLAN hw accel\n", name); return -EOPNOTSUPP; } @@ -342,13 +346,12 @@ static void __vlan_device_event(struct net_device *dev, unsigned long event) case NETDEV_CHANGENAME: vlan_proc_rem_dev(dev); if (vlan_proc_add_dev(dev) < 0) - pr_warning("8021q: failed to change proc name for %s\n", - dev->name); + pr_warn("failed to change proc name for %s\n", + dev->name); break; case NETDEV_REGISTER: if (vlan_proc_add_dev(dev) < 0) - pr_warning("8021q: failed to add proc entry for %s\n", - dev->name); + pr_warn("failed to add proc entry for %s\n", dev->name); break; case NETDEV_UNREGISTER: vlan_proc_rem_dev(dev); @@ -372,7 +375,7 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, if ((event == NETDEV_UP) && (dev->features & NETIF_F_HW_VLAN_FILTER) && dev->netdev_ops->ndo_vlan_rx_add_vid) { - pr_info("8021q: adding VLAN 0 to HW filter on device %s\n", + pr_info("adding VLAN 0 to HW filter on device %s\n", dev->name); dev->netdev_ops->ndo_vlan_rx_add_vid(dev, 0); } @@ -478,9 +481,6 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, if (dev->reg_state != NETREG_UNREGISTERING) break; - /* Delete all VLANs for this dev. */ - grp->killall = 1; - for (i = 0; i < VLAN_N_VID; i++) { vlandev = vlan_group_get_device(grp, i); if (!vlandev) @@ -672,8 +672,7 @@ static int __init vlan_proto_init(void) { int err; - pr_info("%s v%s %s\n", vlan_fullname, vlan_version, vlan_copyright); - pr_info("All bugs added by %s\n", vlan_buggyright); + pr_info("%s v%s\n", vlan_fullname, vlan_version); err = register_pernet_subsys(&vlan_net_ops); if (err < 0) diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h index c3408def8a1..9da07e30d1a 100644 --- a/net/8021q/vlan.h +++ b/net/8021q/vlan.h @@ -118,11 +118,6 @@ extern void vlan_netlink_fini(void); extern struct rtnl_link_ops vlan_link_ops; -static inline int is_vlan_dev(struct net_device *dev) -{ - return dev->priv_flags & IFF_802_1Q_VLAN; -} - extern int vlan_net_id; struct proc_dir_entry; diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index d174c312b7f..1c9aa8c6a77 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -20,6 +20,8 @@ * 2 of the License, or (at your option) any later version. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/slab.h> #include <linux/skbuff.h> @@ -55,7 +57,7 @@ static int vlan_dev_rebuild_header(struct sk_buff *skb) return arp_find(veth->h_dest, skb); #endif default: - pr_debug("%s: unable to resolve type %X addresses.\n", + pr_debug("%s: unable to resolve type %X addresses\n", dev->name, ntohs(veth->h_vlan_encapsulated_proto)); memcpy(veth->h_source, dev->dev_addr, ETH_ALEN); @@ -165,7 +167,7 @@ static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb, u64_stats_update_begin(&stats->syncp); stats->tx_packets++; stats->tx_bytes += len; - u64_stats_update_begin(&stats->syncp); + u64_stats_update_end(&stats->syncp); } else { this_cpu_inc(vlan_dev_info(dev)->vlan_pcpu_stats->tx_dropped); } @@ -314,9 +316,6 @@ static int vlan_dev_stop(struct net_device *dev) struct vlan_dev_info *vlan = vlan_dev_info(dev); struct net_device *real_dev = vlan->real_dev; - if (vlan->flags & VLAN_FLAG_GVRP) - vlan_gvrp_request_leave(dev); - dev_mc_unsync(real_dev, dev); dev_uc_unsync(real_dev, dev); if (dev->flags & IFF_ALLMULTI) @@ -531,7 +530,7 @@ static int vlan_dev_init(struct net_device *dev) (1<<__LINK_STATE_DORMANT))) | (1<<__LINK_STATE_PRESENT); - dev->hw_features = real_dev->vlan_features & NETIF_F_ALL_TX_OFFLOADS; + dev->hw_features = NETIF_F_ALL_TX_OFFLOADS; dev->features |= real_dev->vlan_features | NETIF_F_LLTX; dev->gso_max_size = real_dev->gso_max_size; @@ -590,9 +589,11 @@ static u32 vlan_dev_fix_features(struct net_device *dev, u32 features) { struct net_device *real_dev = vlan_dev_info(dev)->real_dev; - features &= (real_dev->features | NETIF_F_LLTX); + features &= real_dev->features; + features &= real_dev->vlan_features; if (dev_ethtool_get_rx_csum(real_dev)) features |= NETIF_F_RXCSUM; + features |= NETIF_F_LLTX; return features; } diff --git a/net/8021q/vlanproc.c b/net/8021q/vlanproc.c index d940c49d168..016d7f4f1c8 100644 --- a/net/8021q/vlanproc.c +++ b/net/8021q/vlanproc.c @@ -17,6 +17,8 @@ * Jan 20, 1998 Ben Greear Initial Version *****************************************************************************/ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/errno.h> #include <linux/kernel.h> @@ -155,7 +157,7 @@ int __net_init vlan_proc_init(struct net *net) return 0; err: - pr_err("%s: can't create entry in proc filesystem!\n", __func__); + pr_err("can't create entry in proc filesystem!\n"); vlan_proc_cleanup(net); return -ENOBUFS; } diff --git a/net/9p/Kconfig b/net/9p/Kconfig index 7ed75c7bd5d..d9ea09b11cf 100644 --- a/net/9p/Kconfig +++ b/net/9p/Kconfig @@ -3,8 +3,8 @@ # menuconfig NET_9P - depends on NET && EXPERIMENTAL - tristate "Plan 9 Resource Sharing Support (9P2000) (Experimental)" + depends on NET + tristate "Plan 9 Resource Sharing Support (9P2000)" help If you say Y here, you will get experimental support for Plan 9 resource sharing via the 9P2000 protocol. @@ -16,8 +16,8 @@ menuconfig NET_9P if NET_9P config NET_9P_VIRTIO - depends on EXPERIMENTAL && VIRTIO - tristate "9P Virtio Transport (Experimental)" + depends on VIRTIO + tristate "9P Virtio Transport" help This builds support for a transports between guest partitions and a host partition. diff --git a/net/9p/client.c b/net/9p/client.c index 0ce95921860..9e3b0e640da 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -92,9 +92,6 @@ static int get_protocol_version(const substring_t *name) return version; } -static struct p9_req_t * -p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...); - /** * parse_options - parse mount options into client structure * @opts: options string passed from mount @@ -307,12 +304,13 @@ static int p9_tag_init(struct p9_client *c) c->tagpool = p9_idpool_create(); if (IS_ERR(c->tagpool)) { err = PTR_ERR(c->tagpool); - c->tagpool = NULL; goto error; } - - p9_idpool_get(c->tagpool); /* reserve tag 0 */ - + err = p9_idpool_get(c->tagpool); /* reserve tag 0 */ + if (err < 0) { + p9_idpool_destroy(c->tagpool); + goto error; + } c->max_tag = 0; error: return err; @@ -518,12 +516,15 @@ out_err: return err; } +static struct p9_req_t * +p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...); + /** * p9_client_flush - flush (cancel) a request * @c: client state * @oldreq: request to cancel * - * This sents a flush for a particular requests and links + * This sents a flush for a particular request and links * the flush request to the original request. The current * code only supports a single flush request although the protocol * allows for multiple flush requests to be sent for a single request. @@ -614,7 +615,7 @@ p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...) err = c->trans_mod->request(c, req); if (err < 0) { - if (err != -ERESTARTSYS) + if (err != -ERESTARTSYS && err != -EFAULT) c->status = Disconnected; goto reterr; } @@ -789,11 +790,13 @@ struct p9_client *p9_client_create(const char *dev_name, char *options) spin_lock_init(&clnt->lock); INIT_LIST_HEAD(&clnt->fidlist); - p9_tag_init(clnt); + err = p9_tag_init(clnt); + if (err < 0) + goto free_client; err = parse_opts(options, clnt); if (err < 0) - goto free_client; + goto destroy_tagpool; if (!clnt->trans_mod) clnt->trans_mod = v9fs_get_default_trans(); @@ -802,13 +805,12 @@ struct p9_client *p9_client_create(const char *dev_name, char *options) err = -EPROTONOSUPPORT; P9_DPRINTK(P9_DEBUG_ERROR, "No transport defined or default transport\n"); - goto free_client; + goto destroy_tagpool; } clnt->fidpool = p9_idpool_create(); if (IS_ERR(clnt->fidpool)) { err = PTR_ERR(clnt->fidpool); - clnt->fidpool = NULL; goto put_trans; } @@ -834,6 +836,8 @@ destroy_fidpool: p9_idpool_destroy(clnt->fidpool); put_trans: v9fs_put_trans(clnt->trans_mod); +destroy_tagpool: + p9_idpool_destroy(clnt->tagpool); free_client: kfree(clnt); return ERR_PTR(err); @@ -929,15 +933,15 @@ error: } EXPORT_SYMBOL(p9_client_attach); -struct p9_fid *p9_client_walk(struct p9_fid *oldfid, int nwname, char **wnames, - int clone) +struct p9_fid *p9_client_walk(struct p9_fid *oldfid, uint16_t nwname, + char **wnames, int clone) { int err; struct p9_client *clnt; struct p9_fid *fid; struct p9_qid *wqids; struct p9_req_t *req; - int16_t nwqids, count; + uint16_t nwqids, count; err = 0; wqids = NULL; @@ -955,7 +959,7 @@ struct p9_fid *p9_client_walk(struct p9_fid *oldfid, int nwname, char **wnames, fid = oldfid; - P9_DPRINTK(P9_DEBUG_9P, ">>> TWALK fids %d,%d nwname %d wname[0] %s\n", + P9_DPRINTK(P9_DEBUG_9P, ">>> TWALK fids %d,%d nwname %ud wname[0] %s\n", oldfid->fid, fid->fid, nwname, wnames ? wnames[0] : NULL); req = p9_client_rpc(clnt, P9_TWALK, "ddT", oldfid->fid, fid->fid, @@ -1220,27 +1224,6 @@ error: } EXPORT_SYMBOL(p9_client_fsync); -int p9_client_sync_fs(struct p9_fid *fid) -{ - int err = 0; - struct p9_req_t *req; - struct p9_client *clnt; - - P9_DPRINTK(P9_DEBUG_9P, ">>> TSYNC_FS fid %d\n", fid->fid); - - clnt = fid->clnt; - req = p9_client_rpc(clnt, P9_TSYNCFS, "d", fid->fid); - if (IS_ERR(req)) { - err = PTR_ERR(req); - goto error; - } - P9_DPRINTK(P9_DEBUG_9P, "<<< RSYNCFS fid %d\n", fid->fid); - p9_free_req(clnt, req); -error: - return err; -} -EXPORT_SYMBOL(p9_client_sync_fs); - int p9_client_clunk(struct p9_fid *fid) { int err; @@ -1319,7 +1302,7 @@ p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset, if (count < rsize) rsize = count; - /* Don't bother zerocopy form small IO (< 1024) */ + /* Don't bother zerocopy for small IO (< 1024) */ if (((clnt->trans_mod->pref & P9_TRANS_PREF_PAYLOAD_MASK) == P9_TRANS_PREF_PAYLOAD_SEP) && (rsize > 1024)) { req = p9_client_rpc(clnt, P9_TREAD, "dqE", fid->fid, offset, diff --git a/net/9p/mod.c b/net/9p/mod.c index cf8a4128cd5..72c39827505 100644 --- a/net/9p/mod.c +++ b/net/9p/mod.c @@ -139,7 +139,7 @@ void v9fs_put_trans(struct p9_trans_module *m) } /** - * v9fs_init - Initialize module + * init_p9 - Initialize module * */ static int __init init_p9(void) @@ -154,7 +154,7 @@ static int __init init_p9(void) } /** - * v9fs_init - shutdown module + * exit_p9 - shutdown module * */ diff --git a/net/9p/protocol.c b/net/9p/protocol.c index 8a4084fa8b5..a873277cb99 100644 --- a/net/9p/protocol.c +++ b/net/9p/protocol.c @@ -265,7 +265,7 @@ p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt, } break; case 'T':{ - int16_t *nwname = va_arg(ap, int16_t *); + uint16_t *nwname = va_arg(ap, uint16_t *); char ***wnames = va_arg(ap, char ***); errcode = p9pdu_readf(pdu, proto_version, @@ -468,7 +468,8 @@ p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt, case 'E':{ int32_t cnt = va_arg(ap, int32_t); const char *k = va_arg(ap, const void *); - const char *u = va_arg(ap, const void *); + const char __user *u = va_arg(ap, + const void __user *); errcode = p9pdu_writef(pdu, proto_version, "d", cnt); if (!errcode && pdu_write_urw(pdu, k, u, cnt)) @@ -495,7 +496,7 @@ p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt, } break; case 'T':{ - int16_t nwname = va_arg(ap, int); + uint16_t nwname = va_arg(ap, int); const char **wnames = va_arg(ap, const char **); errcode = p9pdu_writef(pdu, proto_version, "w", @@ -673,6 +674,7 @@ int p9dirent_read(char *buf, int len, struct p9_dirent *dirent, } strcpy(dirent->d_name, nameptr); + kfree(nameptr); out: return fake_pdu.offset; diff --git a/net/9p/trans_common.c b/net/9p/trans_common.c index d47880e971d..9a70ebdec56 100644 --- a/net/9p/trans_common.c +++ b/net/9p/trans_common.c @@ -63,10 +63,10 @@ p9_payload_gup(struct p9_req_t *req, size_t *pdata_off, int *pdata_len, int nr_pages, u8 rw) { uint32_t first_page_bytes = 0; - uint32_t pdata_mapped_pages; + int32_t pdata_mapped_pages; struct trans_rpage_info *rpinfo; - *pdata_off = (size_t)req->tc->pubuf & (PAGE_SIZE-1); + *pdata_off = (__force size_t)req->tc->pubuf & (PAGE_SIZE-1); if (*pdata_off) first_page_bytes = min(((size_t)PAGE_SIZE - *pdata_off), @@ -75,14 +75,9 @@ p9_payload_gup(struct p9_req_t *req, size_t *pdata_off, int *pdata_len, rpinfo = req->tc->private; pdata_mapped_pages = get_user_pages_fast((unsigned long)req->tc->pubuf, nr_pages, rw, &rpinfo->rp_data[0]); + if (pdata_mapped_pages <= 0) + return pdata_mapped_pages; - if (pdata_mapped_pages < 0) { - printk(KERN_ERR "get_user_pages_fast failed:%d udata:%p" - "nr_pages:%d\n", pdata_mapped_pages, - req->tc->pubuf, nr_pages); - pdata_mapped_pages = 0; - return -EIO; - } rpinfo->rp_nr_pages = pdata_mapped_pages; if (*pdata_off) { *pdata_len = first_page_bytes; diff --git a/net/9p/trans_fd.c b/net/9p/trans_fd.c index aa5672b15ea..fdfdb5747f6 100644 --- a/net/9p/trans_fd.c +++ b/net/9p/trans_fd.c @@ -716,7 +716,6 @@ static int parse_opts(char *params, struct p9_fd_opts *opts) substring_t args[MAX_OPT_ARGS]; int option; char *options, *tmp_options; - int ret; opts->port = P9_PORT; opts->rfd = ~0; @@ -744,7 +743,6 @@ static int parse_opts(char *params, struct p9_fd_opts *opts) if (r < 0) { P9_DPRINTK(P9_DEBUG_ERROR, "integer field, but no integer?\n"); - ret = r; continue; } } @@ -918,8 +916,8 @@ p9_fd_create_tcp(struct p9_client *client, const char *addr, char *args) sin_server.sin_family = AF_INET; sin_server.sin_addr.s_addr = in_aton(addr); sin_server.sin_port = htons(opts.port); - err = sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, &csocket); - + err = __sock_create(read_pnet(¤t->nsproxy->net_ns), PF_INET, + SOCK_STREAM, IPPROTO_TCP, &csocket, 1); if (err) { P9_EPRINTK(KERN_ERR, "p9_trans_tcp: problem creating socket\n"); return err; @@ -956,7 +954,8 @@ p9_fd_create_unix(struct p9_client *client, const char *addr, char *args) sun_server.sun_family = PF_UNIX; strcpy(sun_server.sun_path, addr); - err = sock_create_kern(PF_UNIX, SOCK_STREAM, 0, &csocket); + err = __sock_create(read_pnet(¤t->nsproxy->net_ns), PF_UNIX, + SOCK_STREAM, 0, &csocket, 1); if (err < 0) { P9_EPRINTK(KERN_ERR, "p9_trans_unix: problem creating socket\n"); return err; diff --git a/net/9p/trans_rdma.c b/net/9p/trans_rdma.c index 150e0c4bbf4..159c50f1c6b 100644 --- a/net/9p/trans_rdma.c +++ b/net/9p/trans_rdma.c @@ -167,7 +167,6 @@ static int parse_opts(char *params, struct p9_rdma_opts *opts) substring_t args[MAX_OPT_ARGS]; int option; char *options, *tmp_options; - int ret; opts->port = P9_PORT; opts->sq_depth = P9_RDMA_SQ_DEPTH; @@ -195,7 +194,6 @@ static int parse_opts(char *params, struct p9_rdma_opts *opts) if (r < 0) { P9_DPRINTK(P9_DEBUG_ERROR, "integer field, but no integer?\n"); - ret = r; continue; } switch (token) { @@ -591,7 +589,8 @@ rdma_create_trans(struct p9_client *client, const char *addr, char *args) return -ENOMEM; /* Create the RDMA CM ID */ - rdma->cm_id = rdma_create_id(p9_cm_event_handler, client, RDMA_PS_TCP); + rdma->cm_id = rdma_create_id(p9_cm_event_handler, client, RDMA_PS_TCP, + IB_QPT_RC); if (IS_ERR(rdma->cm_id)) goto error; diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c index e8f046b0718..244e7074218 100644 --- a/net/9p/trans_virtio.c +++ b/net/9p/trans_virtio.c @@ -326,8 +326,11 @@ req_retry_pinned: outp = pack_sg_list_p(chan->sg, out, VIRTQUEUE_NUM, pdata_off, rpinfo->rp_data, pdata_len); } else { - char *pbuf = req->tc->pubuf ? req->tc->pubuf : - req->tc->pkbuf; + char *pbuf; + if (req->tc->pubuf) + pbuf = (__force char *) req->tc->pubuf; + else + pbuf = req->tc->pkbuf; outp = pack_sg_list(chan->sg, out, VIRTQUEUE_NUM, pbuf, req->tc->pbuf_size); } @@ -352,8 +355,12 @@ req_retry_pinned: in = pack_sg_list_p(chan->sg, out+inp, VIRTQUEUE_NUM, pdata_off, rpinfo->rp_data, pdata_len); } else { - char *pbuf = req->tc->pubuf ? req->tc->pubuf : - req->tc->pkbuf; + char *pbuf; + if (req->tc->pubuf) + pbuf = (__force char *) req->tc->pubuf; + else + pbuf = req->tc->pkbuf; + in = pack_sg_list(chan->sg, out+inp, VIRTQUEUE_NUM, pbuf, req->tc->pbuf_size); } diff --git a/net/9p/util.c b/net/9p/util.c index da6af81e59d..9c1c9348ac3 100644 --- a/net/9p/util.c +++ b/net/9p/util.c @@ -93,7 +93,7 @@ int p9_idpool_get(struct p9_idpool *p) retry: if (idr_pre_get(&p->pool, GFP_NOFS) == 0) - return 0; + return -1; spin_lock_irqsave(&p->lock, flags); diff --git a/net/Kconfig b/net/Kconfig index 79cabf1ee68..878151c772c 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -232,6 +232,20 @@ config XPS depends on SMP && SYSFS && USE_GENERIC_SMP_HELPERS default y +config HAVE_BPF_JIT + bool + +config BPF_JIT + bool "enable BPF Just In Time compiler" + depends on HAVE_BPF_JIT + depends on MODULES + ---help--- + Berkeley Packet Filter filtering capabilities are normally handled + by an interpreter. This option allows kernel to generate a native + code when filter is loaded in memory. This should speedup + packet sniffing (libpcap/tcpdump). Note : Admin should enable + this feature changing /proc/sys/net/core/bpf_jit_enable + menu "Network testing" config NET_PKTGEN diff --git a/net/atm/atm_sysfs.c b/net/atm/atm_sysfs.c index f7fa67c7876..f49da5814bc 100644 --- a/net/atm/atm_sysfs.c +++ b/net/atm/atm_sysfs.c @@ -59,6 +59,14 @@ static ssize_t show_atmaddress(struct device *cdev, return pos - buf; } +static ssize_t show_atmindex(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct atm_dev *adev = to_atm_dev(cdev); + + return sprintf(buf, "%d\n", adev->number); +} + static ssize_t show_carrier(struct device *cdev, struct device_attribute *attr, char *buf) { @@ -99,6 +107,7 @@ static ssize_t show_link_rate(struct device *cdev, static DEVICE_ATTR(address, S_IRUGO, show_address, NULL); static DEVICE_ATTR(atmaddress, S_IRUGO, show_atmaddress, NULL); +static DEVICE_ATTR(atmindex, S_IRUGO, show_atmindex, NULL); static DEVICE_ATTR(carrier, S_IRUGO, show_carrier, NULL); static DEVICE_ATTR(type, S_IRUGO, show_type, NULL); static DEVICE_ATTR(link_rate, S_IRUGO, show_link_rate, NULL); @@ -106,6 +115,7 @@ static DEVICE_ATTR(link_rate, S_IRUGO, show_link_rate, NULL); static struct device_attribute *atm_attrs[] = { &dev_attr_atmaddress, &dev_attr_address, + &dev_attr_atmindex, &dev_attr_carrier, &dev_attr_type, &dev_attr_link_rate, diff --git a/net/atm/lec.c b/net/atm/lec.c index 25073b6ef47..ba48daa68c1 100644 --- a/net/atm/lec.c +++ b/net/atm/lec.c @@ -1171,7 +1171,7 @@ static int __init lane_module_init(void) #endif register_atm_ioctl(&lane_ioctl_ops); - pr_info("lec.c: " __DATE__ " " __TIME__ " initialized\n"); + pr_info("lec.c: initialized\n"); return 0; } diff --git a/net/atm/mpc.c b/net/atm/mpc.c index 644cdf07164..3ccca42e6f9 100644 --- a/net/atm/mpc.c +++ b/net/atm/mpc.c @@ -1482,7 +1482,7 @@ static __init int atm_mpoa_init(void) if (mpc_proc_init() != 0) pr_info("failed to initialize /proc/mpoa\n"); - pr_info("mpc.c: " __DATE__ " " __TIME__ " initialized\n"); + pr_info("mpc.c: initialized\n"); return 0; } diff --git a/net/atm/pppoatm.c b/net/atm/pppoatm.c index e9aced0ec56..db4a11c61d1 100644 --- a/net/atm/pppoatm.c +++ b/net/atm/pppoatm.c @@ -37,6 +37,7 @@ #include <linux/module.h> #include <linux/init.h> +#include <linux/interrupt.h> #include <linux/skbuff.h> #include <linux/slab.h> #include <linux/atm.h> diff --git a/net/atm/proc.c b/net/atm/proc.c index f85da0779e5..be3afdefec5 100644 --- a/net/atm/proc.c +++ b/net/atm/proc.c @@ -191,7 +191,7 @@ static void vcc_info(struct seq_file *seq, struct atm_vcc *vcc) { struct sock *sk = sk_atm(vcc); - seq_printf(seq, "%p ", vcc); + seq_printf(seq, "%pK ", vcc); if (!vcc->dev) seq_printf(seq, "Unassigned "); else @@ -218,7 +218,7 @@ static void svc_info(struct seq_file *seq, struct atm_vcc *vcc) { if (!vcc->dev) seq_printf(seq, sizeof(void *) == 4 ? - "N/A@%p%10s" : "N/A@%p%2s", vcc, ""); + "N/A@%pK%10s" : "N/A@%pK%2s", vcc, ""); else seq_printf(seq, "%3d %3d %5d ", vcc->dev->number, vcc->vpi, vcc->vci); diff --git a/net/batman-adv/aggregation.c b/net/batman-adv/aggregation.c index af45d6b2031..4080970ade7 100644 --- a/net/batman-adv/aggregation.c +++ b/net/batman-adv/aggregation.c @@ -23,20 +23,21 @@ #include "aggregation.h" #include "send.h" #include "routing.h" +#include "hard-interface.h" -/* calculate the size of the hna information for a given packet */ -static int hna_len(struct batman_packet *batman_packet) +/* calculate the size of the tt information for a given packet */ +static int tt_len(const struct batman_packet *batman_packet) { - return batman_packet->num_hna * ETH_ALEN; + return batman_packet->num_tt * ETH_ALEN; } /* return true if new_packet can be aggregated with forw_packet */ -static bool can_aggregate_with(struct batman_packet *new_batman_packet, +static bool can_aggregate_with(const struct batman_packet *new_batman_packet, int packet_len, unsigned long send_time, bool directlink, - struct hard_iface *if_incoming, - struct forw_packet *forw_packet) + const struct hard_iface *if_incoming, + const struct forw_packet *forw_packet) { struct batman_packet *batman_packet = (struct batman_packet *)forw_packet->skb->data; @@ -95,10 +96,10 @@ static bool can_aggregate_with(struct batman_packet *new_batman_packet, return false; } -#define atomic_dec_not_zero(v) atomic_add_unless((v), -1, 0) /* create a new aggregated packet and add this packet to it */ -static void new_aggregated_packet(unsigned char *packet_buff, int packet_len, - unsigned long send_time, bool direct_link, +static void new_aggregated_packet(const unsigned char *packet_buff, + int packet_len, unsigned long send_time, + bool direct_link, struct hard_iface *if_incoming, int own_packet) { @@ -106,20 +107,23 @@ static void new_aggregated_packet(unsigned char *packet_buff, int packet_len, struct forw_packet *forw_packet_aggr; unsigned char *skb_buff; + if (!atomic_inc_not_zero(&if_incoming->refcount)) + return; + /* own packet should always be scheduled */ if (!own_packet) { if (!atomic_dec_not_zero(&bat_priv->batman_queue_left)) { bat_dbg(DBG_BATMAN, bat_priv, "batman packet queue full\n"); - return; + goto out; } } - forw_packet_aggr = kmalloc(sizeof(struct forw_packet), GFP_ATOMIC); + forw_packet_aggr = kmalloc(sizeof(*forw_packet_aggr), GFP_ATOMIC); if (!forw_packet_aggr) { if (!own_packet) atomic_inc(&bat_priv->batman_queue_left); - return; + goto out; } if ((atomic_read(&bat_priv->aggregated_ogms)) && @@ -134,7 +138,7 @@ static void new_aggregated_packet(unsigned char *packet_buff, int packet_len, if (!own_packet) atomic_inc(&bat_priv->batman_queue_left); kfree(forw_packet_aggr); - return; + goto out; } skb_reserve(forw_packet_aggr->skb, sizeof(struct ethhdr)); @@ -147,7 +151,7 @@ static void new_aggregated_packet(unsigned char *packet_buff, int packet_len, forw_packet_aggr->own = own_packet; forw_packet_aggr->if_incoming = if_incoming; forw_packet_aggr->num_packets = 0; - forw_packet_aggr->direct_link_flags = 0; + forw_packet_aggr->direct_link_flags = NO_FLAGS; forw_packet_aggr->send_time = send_time; /* save packet direct link flag status */ @@ -165,12 +169,15 @@ static void new_aggregated_packet(unsigned char *packet_buff, int packet_len, queue_delayed_work(bat_event_workqueue, &forw_packet_aggr->delayed_work, send_time - jiffies); + + return; +out: + hardif_free_ref(if_incoming); } /* aggregate a new packet into the existing aggregation */ static void aggregate(struct forw_packet *forw_packet_aggr, - unsigned char *packet_buff, - int packet_len, + const unsigned char *packet_buff, int packet_len, bool direct_link) { unsigned char *skb_buff; @@ -246,12 +253,13 @@ void add_bat_packet_to_list(struct bat_priv *bat_priv, } /* unpack the aggregated packets and process them one by one */ -void receive_aggr_bat_packet(struct ethhdr *ethhdr, unsigned char *packet_buff, - int packet_len, struct hard_iface *if_incoming) +void receive_aggr_bat_packet(const struct ethhdr *ethhdr, + unsigned char *packet_buff, int packet_len, + struct hard_iface *if_incoming) { struct batman_packet *batman_packet; int buff_pos = 0; - unsigned char *hna_buff; + unsigned char *tt_buff; batman_packet = (struct batman_packet *)packet_buff; @@ -260,14 +268,14 @@ void receive_aggr_bat_packet(struct ethhdr *ethhdr, unsigned char *packet_buff, orig_interval. */ batman_packet->seqno = ntohl(batman_packet->seqno); - hna_buff = packet_buff + buff_pos + BAT_PACKET_LEN; + tt_buff = packet_buff + buff_pos + BAT_PACKET_LEN; receive_bat_packet(ethhdr, batman_packet, - hna_buff, hna_len(batman_packet), + tt_buff, tt_len(batman_packet), if_incoming); - buff_pos += BAT_PACKET_LEN + hna_len(batman_packet); + buff_pos += BAT_PACKET_LEN + tt_len(batman_packet); batman_packet = (struct batman_packet *) (packet_buff + buff_pos); } while (aggregated_packet(buff_pos, packet_len, - batman_packet->num_hna)); + batman_packet->num_tt)); } diff --git a/net/batman-adv/aggregation.h b/net/batman-adv/aggregation.h index 062204289d1..fedeb8d0e13 100644 --- a/net/batman-adv/aggregation.h +++ b/net/batman-adv/aggregation.h @@ -25,9 +25,9 @@ #include "main.h" /* is there another aggregated packet here? */ -static inline int aggregated_packet(int buff_pos, int packet_len, int num_hna) +static inline int aggregated_packet(int buff_pos, int packet_len, int num_tt) { - int next_buff_pos = buff_pos + BAT_PACKET_LEN + (num_hna * ETH_ALEN); + int next_buff_pos = buff_pos + BAT_PACKET_LEN + (num_tt * ETH_ALEN); return (next_buff_pos <= packet_len) && (next_buff_pos <= MAX_AGGREGATION_BYTES); @@ -37,7 +37,8 @@ void add_bat_packet_to_list(struct bat_priv *bat_priv, unsigned char *packet_buff, int packet_len, struct hard_iface *if_incoming, char own_packet, unsigned long send_time); -void receive_aggr_bat_packet(struct ethhdr *ethhdr, unsigned char *packet_buff, - int packet_len, struct hard_iface *if_incoming); +void receive_aggr_bat_packet(const struct ethhdr *ethhdr, + unsigned char *packet_buff, int packet_len, + struct hard_iface *if_incoming); #endif /* _NET_BATMAN_ADV_AGGREGATION_H_ */ diff --git a/net/batman-adv/bat_debugfs.c b/net/batman-adv/bat_debugfs.c index 0e9d4350993..d0af9bf69e4 100644 --- a/net/batman-adv/bat_debugfs.c +++ b/net/batman-adv/bat_debugfs.c @@ -50,7 +50,8 @@ static void emit_log_char(struct debug_log *debug_log, char c) debug_log->log_start = debug_log->log_end - log_buff_len; } -static int fdebug_log(struct debug_log *debug_log, char *fmt, ...) +__printf(2, 3) +static int fdebug_log(struct debug_log *debug_log, const char *fmt, ...) { va_list args; static char debug_log_buf[256]; @@ -74,14 +75,14 @@ static int fdebug_log(struct debug_log *debug_log, char *fmt, ...) return 0; } -int debug_log(struct bat_priv *bat_priv, char *fmt, ...) +int debug_log(struct bat_priv *bat_priv, const char *fmt, ...) { va_list args; char tmp_log_buf[256]; va_start(args, fmt); vscnprintf(tmp_log_buf, sizeof(tmp_log_buf), fmt, args); - fdebug_log(bat_priv->debug_log, "[%10u] %s", + fdebug_log(bat_priv->debug_log, "[%10lu] %s", (jiffies / HZ), tmp_log_buf); va_end(args); @@ -114,7 +115,7 @@ static ssize_t log_read(struct file *file, char __user *buf, !(debug_log->log_end - debug_log->log_start)) return -EAGAIN; - if ((!buf) || (count < 0)) + if (!buf) return -EINVAL; if (count == 0) @@ -184,7 +185,7 @@ static int debug_log_setup(struct bat_priv *bat_priv) if (!bat_priv->debug_dir) goto err; - bat_priv->debug_log = kzalloc(sizeof(struct debug_log), GFP_ATOMIC); + bat_priv->debug_log = kzalloc(sizeof(*bat_priv->debug_log), GFP_ATOMIC); if (!bat_priv->debug_log) goto err; @@ -241,13 +242,13 @@ static int softif_neigh_open(struct inode *inode, struct file *file) static int transtable_global_open(struct inode *inode, struct file *file) { struct net_device *net_dev = (struct net_device *)inode->i_private; - return single_open(file, hna_global_seq_print_text, net_dev); + return single_open(file, tt_global_seq_print_text, net_dev); } static int transtable_local_open(struct inode *inode, struct file *file) { struct net_device *net_dev = (struct net_device *)inode->i_private; - return single_open(file, hna_local_seq_print_text, net_dev); + return single_open(file, tt_local_seq_print_text, net_dev); } static int vis_data_open(struct inode *inode, struct file *file) diff --git a/net/batman-adv/bat_sysfs.c b/net/batman-adv/bat_sysfs.c index e449bf6353e..924d5773da2 100644 --- a/net/batman-adv/bat_sysfs.c +++ b/net/batman-adv/bat_sysfs.c @@ -28,9 +28,17 @@ #include "gateway_client.h" #include "vis.h" -#define to_dev(obj) container_of(obj, struct device, kobj) -#define kobj_to_netdev(obj) to_net_dev(to_dev(obj->parent)) -#define kobj_to_batpriv(obj) netdev_priv(kobj_to_netdev(obj)) +static struct net_device *kobj_to_netdev(struct kobject *obj) +{ + struct device *dev = container_of(obj->parent, struct device, kobj); + return to_net_dev(dev); +} + +static struct bat_priv *kobj_to_batpriv(struct kobject *obj) +{ + struct net_device *net_dev = kobj_to_netdev(obj); + return netdev_priv(net_dev); +} /* Use this, if you have customized show and store functions */ #define BAT_ATTR(_name, _mode, _show, _store) \ @@ -96,7 +104,7 @@ ssize_t show_##_name(struct kobject *kobj, struct attribute *attr, \ static int store_bool_attr(char *buff, size_t count, struct net_device *net_dev, - char *attr_name, atomic_t *attr) + const char *attr_name, atomic_t *attr) { int enabled = -1; @@ -138,16 +146,15 @@ static inline ssize_t __store_bool_attr(char *buff, size_t count, { int ret; - ret = store_bool_attr(buff, count, net_dev, (char *)attr->name, - attr_store); + ret = store_bool_attr(buff, count, net_dev, attr->name, attr_store); if (post_func && ret) post_func(net_dev); return ret; } -static int store_uint_attr(char *buff, size_t count, - struct net_device *net_dev, char *attr_name, +static int store_uint_attr(const char *buff, size_t count, + struct net_device *net_dev, const char *attr_name, unsigned int min, unsigned int max, atomic_t *attr) { unsigned long uint_val; @@ -183,15 +190,15 @@ static int store_uint_attr(char *buff, size_t count, return count; } -static inline ssize_t __store_uint_attr(char *buff, size_t count, +static inline ssize_t __store_uint_attr(const char *buff, size_t count, int min, int max, void (*post_func)(struct net_device *), - struct attribute *attr, + const struct attribute *attr, atomic_t *attr_store, struct net_device *net_dev) { int ret; - ret = store_uint_attr(buff, count, net_dev, (char *)attr->name, + ret = store_uint_attr(buff, count, net_dev, attr->name, min, max, attr_store); if (post_func && ret) post_func(net_dev); @@ -488,22 +495,24 @@ static ssize_t store_mesh_iface(struct kobject *kobj, struct attribute *attr, (strncmp(hard_iface->soft_iface->name, buff, IFNAMSIZ) == 0)) goto out; + if (!rtnl_trylock()) { + ret = -ERESTARTSYS; + goto out; + } + if (status_tmp == IF_NOT_IN_USE) { - rtnl_lock(); hardif_disable_interface(hard_iface); - rtnl_unlock(); - goto out; + goto unlock; } /* if the interface already is in use */ - if (hard_iface->if_status != IF_NOT_IN_USE) { - rtnl_lock(); + if (hard_iface->if_status != IF_NOT_IN_USE) hardif_disable_interface(hard_iface); - rtnl_unlock(); - } ret = hardif_enable_interface(hard_iface, buff); +unlock: + rtnl_unlock(); out: hardif_free_ref(hard_iface); return ret; diff --git a/net/batman-adv/bitarray.c b/net/batman-adv/bitarray.c index ad2ca925b3e..700ee4f7a94 100644 --- a/net/batman-adv/bitarray.c +++ b/net/batman-adv/bitarray.c @@ -26,7 +26,7 @@ /* returns true if the corresponding bit in the given seq_bits indicates true * and curr_seqno is within range of last_seqno */ -uint8_t get_bit_status(unsigned long *seq_bits, uint32_t last_seqno, +uint8_t get_bit_status(const unsigned long *seq_bits, uint32_t last_seqno, uint32_t curr_seqno) { int32_t diff, word_offset, word_num; @@ -130,7 +130,7 @@ static void bit_reset_window(unsigned long *seq_bits) char bit_get_packet(void *priv, unsigned long *seq_bits, int32_t seq_num_diff, int8_t set_mark) { - struct bat_priv *bat_priv = (struct bat_priv *)priv; + struct bat_priv *bat_priv = priv; /* sequence number is slightly older. We already got a sequence number * higher than this one, so we just mark it. */ @@ -190,7 +190,7 @@ char bit_get_packet(void *priv, unsigned long *seq_bits, /* count the hamming weight, how many good packets did we receive? just count * the 1's. */ -int bit_packet_count(unsigned long *seq_bits) +int bit_packet_count(const unsigned long *seq_bits) { int i, hamming = 0; diff --git a/net/batman-adv/bitarray.h b/net/batman-adv/bitarray.h index 769c246d1fc..e32eb2ddd2d 100644 --- a/net/batman-adv/bitarray.h +++ b/net/batman-adv/bitarray.h @@ -26,8 +26,8 @@ /* returns true if the corresponding bit in the given seq_bits indicates true * and curr_seqno is within range of last_seqno */ -uint8_t get_bit_status(unsigned long *seq_bits, uint32_t last_seqno, - uint32_t curr_seqno); +uint8_t get_bit_status(const unsigned long *seq_bits, uint32_t last_seqno, + uint32_t curr_seqno); /* turn corresponding bit on, so we can remember that we got the packet */ void bit_mark(unsigned long *seq_bits, int32_t n); @@ -39,6 +39,6 @@ char bit_get_packet(void *priv, unsigned long *seq_bits, int32_t seq_num_diff, int8_t set_mark); /* count the hamming weight, how many good packets did we receive? */ -int bit_packet_count(unsigned long *seq_bits); +int bit_packet_count(const unsigned long *seq_bits); #endif /* _NET_BATMAN_ADV_BITARRAY_H_ */ diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index 2acd7a666bd..24aee561f3d 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@ -29,18 +29,10 @@ #include <linux/udp.h> #include <linux/if_vlan.h> -static void gw_node_free_rcu(struct rcu_head *rcu) -{ - struct gw_node *gw_node; - - gw_node = container_of(rcu, struct gw_node, rcu); - kfree(gw_node); -} - static void gw_node_free_ref(struct gw_node *gw_node) { if (atomic_dec_and_test(&gw_node->refcount)) - call_rcu(&gw_node->rcu, gw_node_free_rcu); + kfree_rcu(gw_node, rcu); } static struct gw_node *gw_get_selected_gw_node(struct bat_priv *bat_priv) @@ -94,7 +86,7 @@ static void gw_select(struct bat_priv *bat_priv, struct gw_node *new_gw_node) if (new_gw_node && !atomic_inc_not_zero(&new_gw_node->refcount)) new_gw_node = NULL; - curr_gw_node = bat_priv->curr_gw; + curr_gw_node = rcu_dereference_protected(bat_priv->curr_gw, 1); rcu_assign_pointer(bat_priv->curr_gw, new_gw_node); if (curr_gw_node) @@ -127,7 +119,7 @@ void gw_election(struct bat_priv *bat_priv) return; curr_gw = gw_get_selected_gw_node(bat_priv); - if (!curr_gw) + if (curr_gw) goto out; rcu_read_lock(); @@ -281,11 +273,10 @@ static void gw_node_add(struct bat_priv *bat_priv, struct gw_node *gw_node; int down, up; - gw_node = kmalloc(sizeof(struct gw_node), GFP_ATOMIC); + gw_node = kzalloc(sizeof(*gw_node), GFP_ATOMIC); if (!gw_node) return; - memset(gw_node, 0, sizeof(struct gw_node)); INIT_HLIST_NODE(&gw_node->list); gw_node->orig_node = orig_node; atomic_set(&gw_node->refcount, 1); @@ -310,9 +301,13 @@ void gw_node_update(struct bat_priv *bat_priv, struct hlist_node *node; struct gw_node *gw_node, *curr_gw; + /** + * Note: We don't need a NULL check here, since curr_gw never gets + * dereferenced. If curr_gw is NULL we also should not exit as we may + * have this gateway in our list (duplication check!) even though we + * have no currently selected gateway. + */ curr_gw = gw_get_selected_gw_node(bat_priv); - if (!curr_gw) - goto out; rcu_read_lock(); hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) { @@ -327,7 +322,7 @@ void gw_node_update(struct bat_priv *bat_priv, gw_node->deleted = 0; - if (new_gwflags == 0) { + if (new_gwflags == NO_FLAGS) { gw_node->deleted = jiffies; bat_dbg(DBG_BATMAN, bat_priv, "Gateway %pM removed from gateway list\n", @@ -340,7 +335,7 @@ void gw_node_update(struct bat_priv *bat_priv, goto unlock; } - if (new_gwflags == 0) + if (new_gwflags == NO_FLAGS) goto unlock; gw_node_add(bat_priv, orig_node, new_gwflags); @@ -350,14 +345,14 @@ deselect: gw_deselect(bat_priv); unlock: rcu_read_unlock(); -out: + if (curr_gw) gw_node_free_ref(curr_gw); } void gw_node_delete(struct bat_priv *bat_priv, struct orig_node *orig_node) { - return gw_node_update(bat_priv, orig_node, 0); + gw_node_update(bat_priv, orig_node, 0); } void gw_node_purge(struct bat_priv *bat_priv) @@ -398,8 +393,8 @@ void gw_node_purge(struct bat_priv *bat_priv) /** * fails if orig_node has no router */ -static int _write_buffer_text(struct bat_priv *bat_priv, - struct seq_file *seq, struct gw_node *gw_node) +static int _write_buffer_text(struct bat_priv *bat_priv, struct seq_file *seq, + const struct gw_node *gw_node) { struct gw_node *curr_gw; struct neigh_node *router; @@ -435,30 +430,32 @@ int gw_client_seq_print_text(struct seq_file *seq, void *offset) { struct net_device *net_dev = (struct net_device *)seq->private; struct bat_priv *bat_priv = netdev_priv(net_dev); + struct hard_iface *primary_if; struct gw_node *gw_node; struct hlist_node *node; - int gw_count = 0; + int gw_count = 0, ret = 0; - if (!bat_priv->primary_if) { - - return seq_printf(seq, "BATMAN mesh %s disabled - please " - "specify interfaces to enable it\n", - net_dev->name); + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) { + ret = seq_printf(seq, "BATMAN mesh %s disabled - please " + "specify interfaces to enable it\n", + net_dev->name); + goto out; } - if (bat_priv->primary_if->if_status != IF_ACTIVE) { - - return seq_printf(seq, "BATMAN mesh %s disabled - " - "primary interface not active\n", - net_dev->name); + if (primary_if->if_status != IF_ACTIVE) { + ret = seq_printf(seq, "BATMAN mesh %s disabled - " + "primary interface not active\n", + net_dev->name); + goto out; } seq_printf(seq, " %-12s (%s/%i) %17s [%10s]: gw_class ... " "[B.A.T.M.A.N. adv %s%s, MainIF/MAC: %s/%pM (%s)]\n", "Gateway", "#", TQ_MAX_VALUE, "Nexthop", "outgoingIF", SOURCE_VERSION, REVISION_VERSION_STR, - bat_priv->primary_if->net_dev->name, - bat_priv->primary_if->net_dev->dev_addr, net_dev->name); + primary_if->net_dev->name, + primary_if->net_dev->dev_addr, net_dev->name); rcu_read_lock(); hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) { @@ -476,7 +473,10 @@ int gw_client_seq_print_text(struct seq_file *seq, void *offset) if (gw_count == 0) seq_printf(seq, "No gateways in range ...\n"); - return 0; +out: + if (primary_if) + hardif_free_ref(primary_if); + return ret; } int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) @@ -508,7 +508,7 @@ int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) /* check for ip header */ switch (ntohs(ethhdr->h_proto)) { case ETH_P_IP: - if (!pskb_may_pull(skb, header_len + sizeof(struct iphdr))) + if (!pskb_may_pull(skb, header_len + sizeof(*iphdr))) return 0; iphdr = (struct iphdr *)(skb->data + header_len); header_len += iphdr->ihl * 4; @@ -519,10 +519,10 @@ int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) break; case ETH_P_IPV6: - if (!pskb_may_pull(skb, header_len + sizeof(struct ipv6hdr))) + if (!pskb_may_pull(skb, header_len + sizeof(*ipv6hdr))) return 0; ipv6hdr = (struct ipv6hdr *)(skb->data + header_len); - header_len += sizeof(struct ipv6hdr); + header_len += sizeof(*ipv6hdr); /* check for udp header */ if (ipv6hdr->nexthdr != IPPROTO_UDP) @@ -533,10 +533,10 @@ int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) return 0; } - if (!pskb_may_pull(skb, header_len + sizeof(struct udphdr))) + if (!pskb_may_pull(skb, header_len + sizeof(*udphdr))) return 0; udphdr = (struct udphdr *)(skb->data + header_len); - header_len += sizeof(struct udphdr); + header_len += sizeof(*udphdr); /* check for bootp port */ if ((ntohs(ethhdr->h_proto) == ETH_P_IP) && diff --git a/net/batman-adv/gateway_common.c b/net/batman-adv/gateway_common.c index 50d3a59a3d7..e74307be8e0 100644 --- a/net/batman-adv/gateway_common.c +++ b/net/batman-adv/gateway_common.c @@ -76,10 +76,11 @@ void gw_bandwidth_to_kbit(uint8_t gw_srv_class, int *down, int *up) } static bool parse_gw_bandwidth(struct net_device *net_dev, char *buff, - long *up, long *down) + int *up, int *down) { int ret, multi = 1; char *slash_ptr, *tmp_ptr; + long ldown, lup; slash_ptr = strchr(buff, '/'); if (slash_ptr) @@ -96,7 +97,7 @@ static bool parse_gw_bandwidth(struct net_device *net_dev, char *buff, *tmp_ptr = '\0'; } - ret = strict_strtoul(buff, 10, down); + ret = strict_strtol(buff, 10, &ldown); if (ret) { bat_err(net_dev, "Download speed of gateway mode invalid: %s\n", @@ -104,7 +105,7 @@ static bool parse_gw_bandwidth(struct net_device *net_dev, char *buff, return false; } - *down *= multi; + *down = ldown * multi; /* we also got some upload info */ if (slash_ptr) { @@ -121,7 +122,7 @@ static bool parse_gw_bandwidth(struct net_device *net_dev, char *buff, *tmp_ptr = '\0'; } - ret = strict_strtoul(slash_ptr + 1, 10, up); + ret = strict_strtol(slash_ptr + 1, 10, &lup); if (ret) { bat_err(net_dev, "Upload speed of gateway mode invalid: " @@ -129,7 +130,7 @@ static bool parse_gw_bandwidth(struct net_device *net_dev, char *buff, return false; } - *up *= multi; + *up = lup * multi; } return true; @@ -138,7 +139,8 @@ static bool parse_gw_bandwidth(struct net_device *net_dev, char *buff, ssize_t gw_bandwidth_set(struct net_device *net_dev, char *buff, size_t count) { struct bat_priv *bat_priv = netdev_priv(net_dev); - long gw_bandwidth_tmp = 0, up = 0, down = 0; + long gw_bandwidth_tmp = 0; + int up = 0, down = 0; bool ret; ret = parse_gw_bandwidth(net_dev, buff, &up, &down); @@ -158,12 +160,11 @@ ssize_t gw_bandwidth_set(struct net_device *net_dev, char *buff, size_t count) * speeds, hence we need to calculate it back to show the number * that is going to be propagated **/ - gw_bandwidth_to_kbit((uint8_t)gw_bandwidth_tmp, - (int *)&down, (int *)&up); + gw_bandwidth_to_kbit((uint8_t)gw_bandwidth_tmp, &down, &up); gw_deselect(bat_priv); bat_info(net_dev, "Changing gateway bandwidth from: '%i' to: '%ld' " - "(propagating: %ld%s/%ld%s)\n", + "(propagating: %d%s/%d%s)\n", atomic_read(&bat_priv->gw_bandwidth), gw_bandwidth_tmp, (down > 2048 ? down / 1024 : down), (down > 2048 ? "MBit" : "KBit"), diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index b3058e46ee6..abb490106f3 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -31,9 +31,6 @@ #include <linux/if_arp.h> -/* protect update critical side of hardif_list - but not the content */ -static DEFINE_SPINLOCK(hardif_list_lock); - static int batman_skb_recv(struct sk_buff *skb, struct net_device *dev, @@ -49,7 +46,7 @@ void hardif_free_rcu(struct rcu_head *rcu) kfree(hard_iface); } -struct hard_iface *hardif_get_by_netdev(struct net_device *net_dev) +struct hard_iface *hardif_get_by_netdev(const struct net_device *net_dev) { struct hard_iface *hard_iface; @@ -67,7 +64,7 @@ out: return hard_iface; } -static int is_valid_iface(struct net_device *net_dev) +static int is_valid_iface(const struct net_device *net_dev) { if (net_dev->flags & IFF_LOOPBACK) return 0; @@ -89,7 +86,7 @@ static int is_valid_iface(struct net_device *net_dev) return 1; } -static struct hard_iface *hardif_get_active(struct net_device *soft_iface) +static struct hard_iface *hardif_get_active(const struct net_device *soft_iface) { struct hard_iface *hard_iface; @@ -110,50 +107,60 @@ out: return hard_iface; } -static void update_primary_addr(struct bat_priv *bat_priv) +static void primary_if_update_addr(struct bat_priv *bat_priv) { struct vis_packet *vis_packet; + struct hard_iface *primary_if; + + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; vis_packet = (struct vis_packet *) bat_priv->my_vis_info->skb_packet->data; - memcpy(vis_packet->vis_orig, - bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); + memcpy(vis_packet->vis_orig, primary_if->net_dev->dev_addr, ETH_ALEN); memcpy(vis_packet->sender_orig, - bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); + primary_if->net_dev->dev_addr, ETH_ALEN); + +out: + if (primary_if) + hardif_free_ref(primary_if); } -static void set_primary_if(struct bat_priv *bat_priv, - struct hard_iface *hard_iface) +static void primary_if_select(struct bat_priv *bat_priv, + struct hard_iface *new_hard_iface) { + struct hard_iface *curr_hard_iface; struct batman_packet *batman_packet; - struct hard_iface *old_if; - if (hard_iface && !atomic_inc_not_zero(&hard_iface->refcount)) - hard_iface = NULL; + ASSERT_RTNL(); + + if (new_hard_iface && !atomic_inc_not_zero(&new_hard_iface->refcount)) + new_hard_iface = NULL; - old_if = bat_priv->primary_if; - bat_priv->primary_if = hard_iface; + curr_hard_iface = rcu_dereference_protected(bat_priv->primary_if, 1); + rcu_assign_pointer(bat_priv->primary_if, new_hard_iface); - if (old_if) - hardif_free_ref(old_if); + if (curr_hard_iface) + hardif_free_ref(curr_hard_iface); - if (!bat_priv->primary_if) + if (!new_hard_iface) return; - batman_packet = (struct batman_packet *)(hard_iface->packet_buff); + batman_packet = (struct batman_packet *)(new_hard_iface->packet_buff); batman_packet->flags = PRIMARIES_FIRST_HOP; batman_packet->ttl = TTL; - update_primary_addr(bat_priv); + primary_if_update_addr(bat_priv); /*** - * hacky trick to make sure that we send the HNA information via + * hacky trick to make sure that we send the TT information via * our new primary interface */ - atomic_set(&bat_priv->hna_local_changed, 1); + atomic_set(&bat_priv->tt_local_changed, 1); } -static bool hardif_is_iface_up(struct hard_iface *hard_iface) +static bool hardif_is_iface_up(const struct hard_iface *hard_iface) { if (hard_iface->net_dev->flags & IFF_UP) return true; @@ -169,9 +176,9 @@ static void update_mac_addresses(struct hard_iface *hard_iface) hard_iface->net_dev->dev_addr, ETH_ALEN); } -static void check_known_mac_addr(struct net_device *net_dev) +static void check_known_mac_addr(const struct net_device *net_dev) { - struct hard_iface *hard_iface; + const struct hard_iface *hard_iface; rcu_read_lock(); list_for_each_entry_rcu(hard_iface, &hardif_list, list) { @@ -197,8 +204,8 @@ static void check_known_mac_addr(struct net_device *net_dev) int hardif_min_mtu(struct net_device *soft_iface) { - struct bat_priv *bat_priv = netdev_priv(soft_iface); - struct hard_iface *hard_iface; + const struct bat_priv *bat_priv = netdev_priv(soft_iface); + const struct hard_iface *hard_iface; /* allow big frames if all devices are capable to do so * (have MTU > 1500 + BAT_HEADER_LEN) */ int min_mtu = ETH_DATA_LEN; @@ -236,9 +243,10 @@ void update_min_mtu(struct net_device *soft_iface) static void hardif_activate_interface(struct hard_iface *hard_iface) { struct bat_priv *bat_priv; + struct hard_iface *primary_if = NULL; if (hard_iface->if_status != IF_INACTIVE) - return; + goto out; bat_priv = netdev_priv(hard_iface->soft_iface); @@ -249,14 +257,18 @@ static void hardif_activate_interface(struct hard_iface *hard_iface) * the first active interface becomes our primary interface or * the next active interface after the old primay interface was removed */ - if (!bat_priv->primary_if) - set_primary_if(bat_priv, hard_iface); + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) + primary_if_select(bat_priv, hard_iface); bat_info(hard_iface->soft_iface, "Interface activated: %s\n", hard_iface->net_dev->name); update_min_mtu(hard_iface->soft_iface); - return; + +out: + if (primary_if) + hardif_free_ref(primary_if); } static void hardif_deactivate_interface(struct hard_iface *hard_iface) @@ -273,7 +285,8 @@ static void hardif_deactivate_interface(struct hard_iface *hard_iface) update_min_mtu(hard_iface->soft_iface); } -int hardif_enable_interface(struct hard_iface *hard_iface, char *iface_name) +int hardif_enable_interface(struct hard_iface *hard_iface, + const char *iface_name) { struct bat_priv *bat_priv; struct batman_packet *batman_packet; @@ -324,10 +337,10 @@ int hardif_enable_interface(struct hard_iface *hard_iface, char *iface_name) batman_packet = (struct batman_packet *)(hard_iface->packet_buff); batman_packet->packet_type = BAT_PACKET; batman_packet->version = COMPAT_VERSION; - batman_packet->flags = 0; + batman_packet->flags = NO_FLAGS; batman_packet->ttl = 2; batman_packet->tq = TQ_MAX_VALUE; - batman_packet->num_hna = 0; + batman_packet->num_tt = 0; hard_iface->if_num = bat_priv->num_ifaces; bat_priv->num_ifaces++; @@ -386,12 +399,13 @@ err: void hardif_disable_interface(struct hard_iface *hard_iface) { struct bat_priv *bat_priv = netdev_priv(hard_iface->soft_iface); + struct hard_iface *primary_if = NULL; if (hard_iface->if_status == IF_ACTIVE) hardif_deactivate_interface(hard_iface); if (hard_iface->if_status != IF_INACTIVE) - return; + goto out; bat_info(hard_iface->soft_iface, "Removing interface: %s\n", hard_iface->net_dev->name); @@ -400,11 +414,12 @@ void hardif_disable_interface(struct hard_iface *hard_iface) bat_priv->num_ifaces--; orig_hash_del_if(hard_iface, bat_priv->num_ifaces); - if (hard_iface == bat_priv->primary_if) { + primary_if = primary_if_get_selected(bat_priv); + if (hard_iface == primary_if) { struct hard_iface *new_if; new_if = hardif_get_active(hard_iface->soft_iface); - set_primary_if(bat_priv, new_if); + primary_if_select(bat_priv, new_if); if (new_if) hardif_free_ref(new_if); @@ -425,6 +440,10 @@ void hardif_disable_interface(struct hard_iface *hard_iface) hard_iface->soft_iface = NULL; hardif_free_ref(hard_iface); + +out: + if (primary_if) + hardif_free_ref(primary_if); } static struct hard_iface *hardif_add_interface(struct net_device *net_dev) @@ -432,13 +451,15 @@ static struct hard_iface *hardif_add_interface(struct net_device *net_dev) struct hard_iface *hard_iface; int ret; + ASSERT_RTNL(); + ret = is_valid_iface(net_dev); if (ret != 1) goto out; dev_hold(net_dev); - hard_iface = kmalloc(sizeof(struct hard_iface), GFP_ATOMIC); + hard_iface = kmalloc(sizeof(*hard_iface), GFP_ATOMIC); if (!hard_iface) { pr_err("Can't add interface (%s): out of memory\n", net_dev->name); @@ -458,10 +479,7 @@ static struct hard_iface *hardif_add_interface(struct net_device *net_dev) atomic_set(&hard_iface->refcount, 2); check_known_mac_addr(hard_iface->net_dev); - - spin_lock(&hardif_list_lock); list_add_tail_rcu(&hard_iface->list, &hardif_list); - spin_unlock(&hardif_list_lock); return hard_iface; @@ -475,6 +493,8 @@ out: static void hardif_remove_interface(struct hard_iface *hard_iface) { + ASSERT_RTNL(); + /* first deactivate interface */ if (hard_iface->if_status != IF_NOT_IN_USE) hardif_disable_interface(hard_iface); @@ -490,20 +510,11 @@ static void hardif_remove_interface(struct hard_iface *hard_iface) void hardif_remove_interfaces(void) { struct hard_iface *hard_iface, *hard_iface_tmp; - struct list_head if_queue; - - INIT_LIST_HEAD(&if_queue); - spin_lock(&hardif_list_lock); + rtnl_lock(); list_for_each_entry_safe(hard_iface, hard_iface_tmp, &hardif_list, list) { list_del_rcu(&hard_iface->list); - list_add_tail(&hard_iface->list, &if_queue); - } - spin_unlock(&hardif_list_lock); - - rtnl_lock(); - list_for_each_entry_safe(hard_iface, hard_iface_tmp, &if_queue, list) { hardif_remove_interface(hard_iface); } rtnl_unlock(); @@ -512,8 +523,9 @@ void hardif_remove_interfaces(void) static int hard_if_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *net_dev = (struct net_device *)ptr; + struct net_device *net_dev = ptr; struct hard_iface *hard_iface = hardif_get_by_netdev(net_dev); + struct hard_iface *primary_if = NULL; struct bat_priv *bat_priv; if (!hard_iface && event == NETDEV_REGISTER) @@ -531,9 +543,7 @@ static int hard_if_event(struct notifier_block *this, hardif_deactivate_interface(hard_iface); break; case NETDEV_UNREGISTER: - spin_lock(&hardif_list_lock); list_del_rcu(&hard_iface->list); - spin_unlock(&hardif_list_lock); hardif_remove_interface(hard_iface); break; @@ -549,16 +559,22 @@ static int hard_if_event(struct notifier_block *this, update_mac_addresses(hard_iface); bat_priv = netdev_priv(hard_iface->soft_iface); - if (hard_iface == bat_priv->primary_if) - update_primary_addr(bat_priv); + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) + goto hardif_put; + + if (hard_iface == primary_if) + primary_if_update_addr(bat_priv); break; default: break; - }; + } hardif_put: hardif_free_ref(hard_iface); out: + if (primary_if) + hardif_free_ref(primary_if); return NOTIFY_DONE; } diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h index a9ddf36e51c..442eacbc9e3 100644 --- a/net/batman-adv/hard-interface.h +++ b/net/batman-adv/hard-interface.h @@ -22,17 +22,21 @@ #ifndef _NET_BATMAN_ADV_HARD_INTERFACE_H_ #define _NET_BATMAN_ADV_HARD_INTERFACE_H_ -#define IF_NOT_IN_USE 0 -#define IF_TO_BE_REMOVED 1 -#define IF_INACTIVE 2 -#define IF_ACTIVE 3 -#define IF_TO_BE_ACTIVATED 4 -#define IF_I_WANT_YOU 5 +enum hard_if_state { + IF_NOT_IN_USE, + IF_TO_BE_REMOVED, + IF_INACTIVE, + IF_ACTIVE, + IF_TO_BE_ACTIVATED, + IF_I_WANT_YOU +}; extern struct notifier_block hard_if_notifier; -struct hard_iface *hardif_get_by_netdev(struct net_device *net_dev); -int hardif_enable_interface(struct hard_iface *hard_iface, char *iface_name); +struct hard_iface* +hardif_get_by_netdev(const struct net_device *net_dev); +int hardif_enable_interface(struct hard_iface *hard_iface, + const char *iface_name); void hardif_disable_interface(struct hard_iface *hard_iface); void hardif_remove_interfaces(void); int hardif_min_mtu(struct net_device *soft_iface); @@ -45,4 +49,22 @@ static inline void hardif_free_ref(struct hard_iface *hard_iface) call_rcu(&hard_iface->rcu, hardif_free_rcu); } +static inline struct hard_iface *primary_if_get_selected( + struct bat_priv *bat_priv) +{ + struct hard_iface *hard_iface; + + rcu_read_lock(); + hard_iface = rcu_dereference(bat_priv->primary_if); + if (!hard_iface) + goto out; + + if (!atomic_inc_not_zero(&hard_iface->refcount)) + hard_iface = NULL; + +out: + rcu_read_unlock(); + return hard_iface; +} + #endif /* _NET_BATMAN_ADV_HARD_INTERFACE_H_ */ diff --git a/net/batman-adv/hash.c b/net/batman-adv/hash.c index c5213d8f2cc..2a172505f51 100644 --- a/net/batman-adv/hash.c +++ b/net/batman-adv/hash.c @@ -46,15 +46,16 @@ struct hashtable_t *hash_new(int size) { struct hashtable_t *hash; - hash = kmalloc(sizeof(struct hashtable_t), GFP_ATOMIC); + hash = kmalloc(sizeof(*hash), GFP_ATOMIC); if (!hash) return NULL; - hash->table = kmalloc(sizeof(struct element_t *) * size, GFP_ATOMIC); + hash->table = kmalloc(sizeof(*hash->table) * size, GFP_ATOMIC); if (!hash->table) goto free_hash; - hash->list_locks = kmalloc(sizeof(spinlock_t) * size, GFP_ATOMIC); + hash->list_locks = kmalloc(sizeof(*hash->list_locks) * size, + GFP_ATOMIC); if (!hash->list_locks) goto free_table; diff --git a/net/batman-adv/hash.h b/net/batman-adv/hash.h index 434822b2747..dd5c9fd7a90 100644 --- a/net/batman-adv/hash.h +++ b/net/batman-adv/hash.h @@ -28,12 +28,12 @@ * compare 2 element datas for their keys, * return 0 if same and not 0 if not * same */ -typedef int (*hashdata_compare_cb)(struct hlist_node *, void *); +typedef int (*hashdata_compare_cb)(const struct hlist_node *, const void *); /* the hashfunction, should return an index * based on the key in the data of the first * argument and the size the second */ -typedef int (*hashdata_choose_cb)(void *, int); +typedef int (*hashdata_choose_cb)(const void *, int); typedef void (*hashdata_free_cb)(struct hlist_node *, void *); struct hashtable_t { @@ -80,7 +80,7 @@ static inline void hash_delete(struct hashtable_t *hash, static inline int hash_add(struct hashtable_t *hash, hashdata_compare_cb compare, hashdata_choose_cb choose, - void *data, struct hlist_node *data_node) + const void *data, struct hlist_node *data_node) { int index; struct hlist_head *head; diff --git a/net/batman-adv/icmp_socket.c b/net/batman-adv/icmp_socket.c index 49079c25447..ac3520e057c 100644 --- a/net/batman-adv/icmp_socket.c +++ b/net/batman-adv/icmp_socket.c @@ -46,7 +46,7 @@ static int bat_socket_open(struct inode *inode, struct file *file) nonseekable_open(inode, file); - socket_client = kmalloc(sizeof(struct socket_client), GFP_KERNEL); + socket_client = kmalloc(sizeof(*socket_client), GFP_KERNEL); if (!socket_client) return -ENOMEM; @@ -153,6 +153,7 @@ static ssize_t bat_socket_write(struct file *file, const char __user *buff, { struct socket_client *socket_client = file->private_data; struct bat_priv *bat_priv = socket_client->bat_priv; + struct hard_iface *primary_if = NULL; struct sk_buff *skb; struct icmp_packet_rr *icmp_packet; @@ -167,15 +168,21 @@ static ssize_t bat_socket_write(struct file *file, const char __user *buff, return -EINVAL; } - if (!bat_priv->primary_if) - return -EFAULT; + primary_if = primary_if_get_selected(bat_priv); + + if (!primary_if) { + len = -EFAULT; + goto out; + } if (len >= sizeof(struct icmp_packet_rr)) packet_len = sizeof(struct icmp_packet_rr); skb = dev_alloc_skb(packet_len + sizeof(struct ethhdr)); - if (!skb) - return -ENOMEM; + if (!skb) { + len = -ENOMEM; + goto out; + } skb_reserve(skb, sizeof(struct ethhdr)); icmp_packet = (struct icmp_packet_rr *)skb_put(skb, packet_len); @@ -233,7 +240,7 @@ static ssize_t bat_socket_write(struct file *file, const char __user *buff, goto dst_unreach; memcpy(icmp_packet->orig, - bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); + primary_if->net_dev->dev_addr, ETH_ALEN); if (packet_len == sizeof(struct icmp_packet_rr)) memcpy(icmp_packet->rr, @@ -248,6 +255,8 @@ dst_unreach: free_skb: kfree_skb(skb); out: + if (primary_if) + hardif_free_ref(primary_if); if (neigh_node) neigh_node_free_ref(neigh_node); if (orig_node) @@ -301,7 +310,7 @@ static void bat_socket_add_packet(struct socket_client *socket_client, { struct socket_packet *socket_packet; - socket_packet = kmalloc(sizeof(struct socket_packet), GFP_ATOMIC); + socket_packet = kmalloc(sizeof(*socket_packet), GFP_ATOMIC); if (!socket_packet) return; diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index 709b33bbdf4..2d6445e171d 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -33,6 +33,9 @@ #include "vis.h" #include "hash.h" + +/* List manipulations on hardif_list have to be rtnl_lock()'ed, + * list traversals just rcu-locked */ struct list_head hardif_list; unsigned char broadcast_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; @@ -81,28 +84,29 @@ int mesh_init(struct net_device *soft_iface) spin_lock_init(&bat_priv->forw_bat_list_lock); spin_lock_init(&bat_priv->forw_bcast_list_lock); - spin_lock_init(&bat_priv->hna_lhash_lock); - spin_lock_init(&bat_priv->hna_ghash_lock); + spin_lock_init(&bat_priv->tt_lhash_lock); + spin_lock_init(&bat_priv->tt_ghash_lock); spin_lock_init(&bat_priv->gw_list_lock); spin_lock_init(&bat_priv->vis_hash_lock); spin_lock_init(&bat_priv->vis_list_lock); spin_lock_init(&bat_priv->softif_neigh_lock); + spin_lock_init(&bat_priv->softif_neigh_vid_lock); INIT_HLIST_HEAD(&bat_priv->forw_bat_list); INIT_HLIST_HEAD(&bat_priv->forw_bcast_list); INIT_HLIST_HEAD(&bat_priv->gw_list); - INIT_HLIST_HEAD(&bat_priv->softif_neigh_list); + INIT_HLIST_HEAD(&bat_priv->softif_neigh_vids); if (originator_init(bat_priv) < 1) goto err; - if (hna_local_init(bat_priv) < 1) + if (tt_local_init(bat_priv) < 1) goto err; - if (hna_global_init(bat_priv) < 1) + if (tt_global_init(bat_priv) < 1) goto err; - hna_local_add(soft_iface, soft_iface->dev_addr); + tt_local_add(soft_iface, soft_iface->dev_addr); if (vis_init(bat_priv) < 1) goto err; @@ -133,8 +137,8 @@ void mesh_free(struct net_device *soft_iface) gw_node_purge(bat_priv); originator_free(bat_priv); - hna_local_free(bat_priv); - hna_global_free(bat_priv); + tt_local_free(bat_priv); + tt_global_free(bat_priv); softif_neigh_purge(bat_priv); @@ -151,9 +155,9 @@ void dec_module_count(void) module_put(THIS_MODULE); } -int is_my_mac(uint8_t *addr) +int is_my_mac(const uint8_t *addr) { - struct hard_iface *hard_iface; + const struct hard_iface *hard_iface; rcu_read_lock(); list_for_each_entry_rcu(hard_iface, &hardif_list, list) { diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index dc248697de7..ed488cbae80 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -34,46 +34,51 @@ #define TQ_MAX_VALUE 255 #define JITTER 20 -#define TTL 50 /* Time To Live of broadcast messages */ -#define PURGE_TIMEOUT 200 /* purge originators after time in seconds if no - * valid packet comes in -> TODO: check - * influence on TQ_LOCAL_WINDOW_SIZE */ -#define LOCAL_HNA_TIMEOUT 3600 /* in seconds */ + /* Time To Live of broadcast messages */ +#define TTL 50 -#define TQ_LOCAL_WINDOW_SIZE 64 /* sliding packet range of received originator - * messages in squence numbers (should be a - * multiple of our word size) */ +/* purge originators after time in seconds if no valid packet comes in + * -> TODO: check influence on TQ_LOCAL_WINDOW_SIZE */ +#define PURGE_TIMEOUT 200 +#define TT_LOCAL_TIMEOUT 3600 /* in seconds */ + +/* sliding packet range of received originator messages in squence numbers + * (should be a multiple of our word size) */ +#define TQ_LOCAL_WINDOW_SIZE 64 #define TQ_GLOBAL_WINDOW_SIZE 5 #define TQ_LOCAL_BIDRECT_SEND_MINIMUM 1 #define TQ_LOCAL_BIDRECT_RECV_MINIMUM 1 #define TQ_TOTAL_BIDRECT_LIMIT 1 +#define NO_FLAGS 0 + #define NUM_WORDS (TQ_LOCAL_WINDOW_SIZE / WORD_BIT_SIZE) #define LOG_BUF_LEN 8192 /* has to be a power of 2 */ #define VIS_INTERVAL 5000 /* 5 seconds */ -/* how much worse secondary interfaces may be to - * to be considered as bonding candidates */ - +/* how much worse secondary interfaces may be to be considered as bonding + * candidates */ #define BONDING_TQ_THRESHOLD 50 -#define MAX_AGGREGATION_BYTES 512 /* should not be bigger than 512 bytes or - * change the size of - * forw_packet->direct_link_flags */ +/* should not be bigger than 512 bytes or change the size of + * forw_packet->direct_link_flags */ +#define MAX_AGGREGATION_BYTES 512 #define MAX_AGGREGATION_MS 100 #define SOFTIF_NEIGH_TIMEOUT 180000 /* 3 minutes */ +/* don't reset again within 30 seconds */ #define RESET_PROTECTION_MS 30000 #define EXPECTED_SEQNO_RANGE 65536 -/* don't reset again within 30 seconds */ -#define MESH_INACTIVE 0 -#define MESH_ACTIVE 1 -#define MESH_DEACTIVATING 2 +enum mesh_state { + MESH_INACTIVE, + MESH_ACTIVE, + MESH_DEACTIVATING +}; #define BCAST_QUEUE_LEN 256 #define BATMAN_QUEUE_LEN 256 @@ -84,13 +89,15 @@ #ifdef pr_fmt #undef pr_fmt #endif -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt /* Append 'batman-adv: ' before - * kernel messages */ +/* Append 'batman-adv: ' before kernel messages */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define DBG_BATMAN 1 /* all messages related to routing / flooding / - * broadcasting / etc */ -#define DBG_ROUTES 2 /* route or hna added / changed / deleted */ -#define DBG_ALL 3 +/* all messages related to routing / flooding / broadcasting / etc */ +enum dbg_level { + DBG_BATMAN = 1 << 0, + DBG_ROUTES = 1 << 1, /* route added / changed / deleted */ + DBG_ALL = 3 +}; /* @@ -131,10 +138,10 @@ int mesh_init(struct net_device *soft_iface); void mesh_free(struct net_device *soft_iface); void inc_module_count(void); void dec_module_count(void); -int is_my_mac(uint8_t *addr); +int is_my_mac(const uint8_t *addr); #ifdef CONFIG_BATMAN_ADV_DEBUG -int debug_log(struct bat_priv *bat_priv, char *fmt, ...); +int debug_log(struct bat_priv *bat_priv, const char *fmt, ...) __printf(2, 3); #define bat_dbg(type, bat_priv, fmt, arg...) \ do { \ @@ -143,9 +150,10 @@ int debug_log(struct bat_priv *bat_priv, char *fmt, ...); } \ while (0) #else /* !CONFIG_BATMAN_ADV_DEBUG */ +__printf(3, 4) static inline void bat_dbg(char type __always_unused, struct bat_priv *bat_priv __always_unused, - char *fmt __always_unused, ...) + const char *fmt __always_unused, ...) { } #endif @@ -170,9 +178,32 @@ static inline void bat_dbg(char type __always_unused, * * note: can't use compare_ether_addr() as it requires aligned memory */ -static inline int compare_eth(void *data1, void *data2) + +static inline int compare_eth(const void *data1, const void *data2) { return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); } + +#define atomic_dec_not_zero(v) atomic_add_unless((v), -1, 0) + +/* Returns the smallest signed integer in two's complement with the sizeof x */ +#define smallest_signed_int(x) (1u << (7u + 8u * (sizeof(x) - 1u))) + +/* Checks if a sequence number x is a predecessor/successor of y. + * they handle overflows/underflows and can correctly check for a + * predecessor/successor unless the variable sequence number has grown by + * more then 2**(bitwidth(x)-1)-1. + * This means that for a uint8_t with the maximum value 255, it would think: + * - when adding nothing - it is neither a predecessor nor a successor + * - before adding more than 127 to the starting value - it is a predecessor, + * - when adding 128 - it is neither a predecessor nor a successor, + * - after adding more than 127 to the starting value - it is a successor */ +#define seq_before(x, y) ({typeof(x) _d1 = (x); \ + typeof(y) _d2 = (y); \ + typeof(x) _dummy = (_d1 - _d2); \ + (void) (&_d1 == &_d2); \ + _dummy > smallest_signed_int(_dummy); }) +#define seq_after(x, y) seq_before(y, x) + #endif /* _NET_BATMAN_ADV_MAIN_H_ */ diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index 5b8fe32043d..a6c35d4e332 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@ -19,8 +19,6 @@ * */ -/* increase the reference counter for this originator */ - #include "main.h" #include "originator.h" #include "hash.h" @@ -56,18 +54,10 @@ err: return 0; } -static void neigh_node_free_rcu(struct rcu_head *rcu) -{ - struct neigh_node *neigh_node; - - neigh_node = container_of(rcu, struct neigh_node, rcu); - kfree(neigh_node); -} - void neigh_node_free_ref(struct neigh_node *neigh_node) { if (atomic_dec_and_test(&neigh_node->refcount)) - call_rcu(&neigh_node->rcu, neigh_node_free_rcu); + kfree_rcu(neigh_node, rcu); } /* increases the refcounter of a found router */ @@ -87,7 +77,7 @@ struct neigh_node *orig_node_get_router(struct orig_node *orig_node) struct neigh_node *create_neighbor(struct orig_node *orig_node, struct orig_node *orig_neigh_node, - uint8_t *neigh, + const uint8_t *neigh, struct hard_iface *if_incoming) { struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); @@ -96,7 +86,7 @@ struct neigh_node *create_neighbor(struct orig_node *orig_node, bat_dbg(DBG_BATMAN, bat_priv, "Creating new last-hop neighbor of originator\n"); - neigh_node = kzalloc(sizeof(struct neigh_node), GFP_ATOMIC); + neigh_node = kzalloc(sizeof(*neigh_node), GFP_ATOMIC); if (!neigh_node) return NULL; @@ -144,7 +134,7 @@ static void orig_node_free_rcu(struct rcu_head *rcu) spin_unlock_bh(&orig_node->neigh_list_lock); frag_list_free(&orig_node->frag_list); - hna_global_del_orig(orig_node->bat_priv, orig_node, + tt_global_del_orig(orig_node->bat_priv, orig_node, "originator timed out"); kfree(orig_node->bcast_own); @@ -193,7 +183,7 @@ void originator_free(struct bat_priv *bat_priv) /* this function finds or creates an originator entry for the given * address if it does not exits */ -struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr) +struct orig_node *get_orig_node(struct bat_priv *bat_priv, const uint8_t *addr) { struct orig_node *orig_node; int size; @@ -206,7 +196,7 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr) bat_dbg(DBG_BATMAN, bat_priv, "Creating new originator: %pM\n", addr); - orig_node = kzalloc(sizeof(struct orig_node), GFP_ATOMIC); + orig_node = kzalloc(sizeof(*orig_node), GFP_ATOMIC); if (!orig_node) return NULL; @@ -222,7 +212,7 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr) orig_node->bat_priv = bat_priv; memcpy(orig_node->orig, addr, ETH_ALEN); orig_node->router = NULL; - orig_node->hna_buff = NULL; + orig_node->tt_buff = NULL; orig_node->bcast_seqno_reset = jiffies - 1 - msecs_to_jiffies(RESET_PROTECTION_MS); orig_node->batman_seqno_reset = jiffies - 1 @@ -333,8 +323,8 @@ static bool purge_orig_node(struct bat_priv *bat_priv, &best_neigh_node)) { update_routes(bat_priv, orig_node, best_neigh_node, - orig_node->hna_buff, - orig_node->hna_buff_len); + orig_node->tt_buff, + orig_node->tt_buff_len); } } @@ -405,29 +395,34 @@ int orig_seq_print_text(struct seq_file *seq, void *offset) struct hashtable_t *hash = bat_priv->orig_hash; struct hlist_node *node, *node_tmp; struct hlist_head *head; + struct hard_iface *primary_if; struct orig_node *orig_node; struct neigh_node *neigh_node, *neigh_node_tmp; int batman_count = 0; int last_seen_secs; int last_seen_msecs; - int i; + int i, ret = 0; - if ((!bat_priv->primary_if) || - (bat_priv->primary_if->if_status != IF_ACTIVE)) { - if (!bat_priv->primary_if) - return seq_printf(seq, "BATMAN mesh %s disabled - " - "please specify interfaces to enable it\n", - net_dev->name); + primary_if = primary_if_get_selected(bat_priv); - return seq_printf(seq, "BATMAN mesh %s " - "disabled - primary interface not active\n", - net_dev->name); + if (!primary_if) { + ret = seq_printf(seq, "BATMAN mesh %s disabled - " + "please specify interfaces to enable it\n", + net_dev->name); + goto out; + } + + if (primary_if->if_status != IF_ACTIVE) { + ret = seq_printf(seq, "BATMAN mesh %s " + "disabled - primary interface not active\n", + net_dev->name); + goto out; } seq_printf(seq, "[B.A.T.M.A.N. adv %s%s, MainIF/MAC: %s/%pM (%s)]\n", SOURCE_VERSION, REVISION_VERSION_STR, - bat_priv->primary_if->net_dev->name, - bat_priv->primary_if->net_dev->dev_addr, net_dev->name); + primary_if->net_dev->name, + primary_if->net_dev->dev_addr, net_dev->name); seq_printf(seq, " %-15s %s (%s/%i) %17s [%10s]: %20s ...\n", "Originator", "last-seen", "#", TQ_MAX_VALUE, "Nexthop", "outgoingIF", "Potential nexthops"); @@ -474,7 +469,10 @@ next: if (batman_count == 0) seq_printf(seq, "No batman nodes in range ...\n"); - return 0; +out: + if (primary_if) + hardif_free_ref(primary_if); + return ret; } static int orig_node_add_if(struct orig_node *orig_node, int max_if_num) @@ -561,7 +559,7 @@ static int orig_node_del_if(struct orig_node *orig_node, memcpy(data_ptr, orig_node->bcast_own, del_if_num * chunk_size); /* copy second part */ - memcpy(data_ptr + del_if_num * chunk_size, + memcpy((char *)data_ptr + del_if_num * chunk_size, orig_node->bcast_own + ((del_if_num + 1) * chunk_size), (max_if_num - del_if_num) * chunk_size); @@ -581,7 +579,7 @@ free_bcast_own: memcpy(data_ptr, orig_node->bcast_own_sum, del_if_num * sizeof(uint8_t)); - memcpy(data_ptr + del_if_num * sizeof(uint8_t), + memcpy((char *)data_ptr + del_if_num * sizeof(uint8_t), orig_node->bcast_own_sum + ((del_if_num + 1) * sizeof(uint8_t)), (max_if_num - del_if_num) * sizeof(uint8_t)); diff --git a/net/batman-adv/originator.h b/net/batman-adv/originator.h index e1d641f27aa..8e307af7aa0 100644 --- a/net/batman-adv/originator.h +++ b/net/batman-adv/originator.h @@ -28,10 +28,10 @@ int originator_init(struct bat_priv *bat_priv); void originator_free(struct bat_priv *bat_priv); void purge_orig_ref(struct bat_priv *bat_priv); void orig_node_free_ref(struct orig_node *orig_node); -struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr); +struct orig_node *get_orig_node(struct bat_priv *bat_priv, const uint8_t *addr); struct neigh_node *create_neighbor(struct orig_node *orig_node, struct orig_node *orig_neigh_node, - uint8_t *neigh, + const uint8_t *neigh, struct hard_iface *if_incoming); void neigh_node_free_ref(struct neigh_node *neigh_node); struct neigh_node *orig_node_get_router(struct orig_node *orig_node); @@ -41,18 +41,18 @@ int orig_hash_del_if(struct hard_iface *hard_iface, int max_if_num); /* returns 1 if they are the same originator */ -static inline int compare_orig(struct hlist_node *node, void *data2) +static inline int compare_orig(const struct hlist_node *node, const void *data2) { - void *data1 = container_of(node, struct orig_node, hash_entry); + const void *data1 = container_of(node, struct orig_node, hash_entry); return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); } /* hashfunction to choose an entry in a hash table of given size */ /* hash algorithm from http://en.wikipedia.org/wiki/Hash_table */ -static inline int choose_orig(void *data, int32_t size) +static inline int choose_orig(const void *data, int32_t size) { - unsigned char *key = data; + const unsigned char *key = data; uint32_t hash = 0; size_t i; @@ -70,7 +70,7 @@ static inline int choose_orig(void *data, int32_t size) } static inline struct orig_node *orig_hash_find(struct bat_priv *bat_priv, - void *data) + const void *data) { struct hashtable_t *hash = bat_priv->orig_hash; struct hlist_head *head; diff --git a/net/batman-adv/packet.h b/net/batman-adv/packet.h index e7571879af3..9f77086a546 100644 --- a/net/batman-adv/packet.h +++ b/net/batman-adv/packet.h @@ -24,33 +24,44 @@ #define ETH_P_BATMAN 0x4305 /* unofficial/not registered Ethertype */ -#define BAT_PACKET 0x01 -#define BAT_ICMP 0x02 -#define BAT_UNICAST 0x03 -#define BAT_BCAST 0x04 -#define BAT_VIS 0x05 -#define BAT_UNICAST_FRAG 0x06 +enum bat_packettype { + BAT_PACKET = 0x01, + BAT_ICMP = 0x02, + BAT_UNICAST = 0x03, + BAT_BCAST = 0x04, + BAT_VIS = 0x05, + BAT_UNICAST_FRAG = 0x06 +}; /* this file is included by batctl which needs these defines */ #define COMPAT_VERSION 12 -#define DIRECTLINK 0x40 -#define VIS_SERVER 0x20 -#define PRIMARIES_FIRST_HOP 0x10 + +enum batman_flags { + PRIMARIES_FIRST_HOP = 1 << 4, + VIS_SERVER = 1 << 5, + DIRECTLINK = 1 << 6 +}; /* ICMP message types */ -#define ECHO_REPLY 0 -#define DESTINATION_UNREACHABLE 3 -#define ECHO_REQUEST 8 -#define TTL_EXCEEDED 11 -#define PARAMETER_PROBLEM 12 +enum icmp_packettype { + ECHO_REPLY = 0, + DESTINATION_UNREACHABLE = 3, + ECHO_REQUEST = 8, + TTL_EXCEEDED = 11, + PARAMETER_PROBLEM = 12 +}; /* vis defines */ -#define VIS_TYPE_SERVER_SYNC 0 -#define VIS_TYPE_CLIENT_UPDATE 1 +enum vis_packettype { + VIS_TYPE_SERVER_SYNC = 0, + VIS_TYPE_CLIENT_UPDATE = 1 +}; /* fragmentation defines */ -#define UNI_FRAG_HEAD 0x01 -#define UNI_FRAG_LARGETAIL 0x02 +enum unicast_frag_flags { + UNI_FRAG_HEAD = 1 << 0, + UNI_FRAG_LARGETAIL = 1 << 1 +}; struct batman_packet { uint8_t packet_type; @@ -61,7 +72,7 @@ struct batman_packet { uint8_t orig[6]; uint8_t prev_sender[6]; uint8_t ttl; - uint8_t num_hna; + uint8_t num_tt; uint8_t gw_flags; /* flags related to gateway class */ uint8_t align; } __packed; @@ -128,8 +139,7 @@ struct vis_packet { uint8_t entries; /* number of entries behind this struct */ uint32_t seqno; /* sequence number */ uint8_t ttl; /* TTL */ - uint8_t vis_orig[6]; /* originator that informs about its - * neighbors */ + uint8_t vis_orig[6]; /* originator that announces its neighbors */ uint8_t target_orig[6]; /* who should receive this packet */ uint8_t sender_orig[6]; /* who sent or rebroadcasted this packet */ } __packed; diff --git a/net/batman-adv/ring_buffer.c b/net/batman-adv/ring_buffer.c index 5bb6a619afe..f1ccfa76ce8 100644 --- a/net/batman-adv/ring_buffer.c +++ b/net/batman-adv/ring_buffer.c @@ -28,9 +28,9 @@ void ring_buffer_set(uint8_t lq_recv[], uint8_t *lq_index, uint8_t value) *lq_index = (*lq_index + 1) % TQ_GLOBAL_WINDOW_SIZE; } -uint8_t ring_buffer_avg(uint8_t lq_recv[]) +uint8_t ring_buffer_avg(const uint8_t lq_recv[]) { - uint8_t *ptr; + const uint8_t *ptr; uint16_t count = 0, i = 0, sum = 0; ptr = lq_recv; diff --git a/net/batman-adv/ring_buffer.h b/net/batman-adv/ring_buffer.h index 0395b274186..7cdfe62b657 100644 --- a/net/batman-adv/ring_buffer.h +++ b/net/batman-adv/ring_buffer.h @@ -23,6 +23,6 @@ #define _NET_BATMAN_ADV_RING_BUFFER_H_ void ring_buffer_set(uint8_t lq_recv[], uint8_t *lq_index, uint8_t value); -uint8_t ring_buffer_avg(uint8_t lq_recv[]); +uint8_t ring_buffer_avg(const uint8_t lq_recv[]); #endif /* _NET_BATMAN_ADV_RING_BUFFER_H_ */ diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index f6c64224697..934f1f2f86c 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -64,28 +64,27 @@ void slide_own_bcast_window(struct hard_iface *hard_iface) } } -static void update_HNA(struct bat_priv *bat_priv, struct orig_node *orig_node, - unsigned char *hna_buff, int hna_buff_len) +static void update_TT(struct bat_priv *bat_priv, struct orig_node *orig_node, + const unsigned char *tt_buff, int tt_buff_len) { - if ((hna_buff_len != orig_node->hna_buff_len) || - ((hna_buff_len > 0) && - (orig_node->hna_buff_len > 0) && - (memcmp(orig_node->hna_buff, hna_buff, hna_buff_len) != 0))) { - - if (orig_node->hna_buff_len > 0) - hna_global_del_orig(bat_priv, orig_node, - "originator changed hna"); - - if ((hna_buff_len > 0) && (hna_buff)) - hna_global_add_orig(bat_priv, orig_node, - hna_buff, hna_buff_len); + if ((tt_buff_len != orig_node->tt_buff_len) || + ((tt_buff_len > 0) && + (orig_node->tt_buff_len > 0) && + (memcmp(orig_node->tt_buff, tt_buff, tt_buff_len) != 0))) { + + if (orig_node->tt_buff_len > 0) + tt_global_del_orig(bat_priv, orig_node, + "originator changed tt"); + + if ((tt_buff_len > 0) && (tt_buff)) + tt_global_add_orig(bat_priv, orig_node, + tt_buff, tt_buff_len); } } -static void update_route(struct bat_priv *bat_priv, - struct orig_node *orig_node, +static void update_route(struct bat_priv *bat_priv, struct orig_node *orig_node, struct neigh_node *neigh_node, - unsigned char *hna_buff, int hna_buff_len) + const unsigned char *tt_buff, int tt_buff_len) { struct neigh_node *curr_router; @@ -96,7 +95,7 @@ static void update_route(struct bat_priv *bat_priv, bat_dbg(DBG_ROUTES, bat_priv, "Deleting route towards: %pM\n", orig_node->orig); - hna_global_del_orig(bat_priv, orig_node, + tt_global_del_orig(bat_priv, orig_node, "originator timed out"); /* route added */ @@ -105,11 +104,11 @@ static void update_route(struct bat_priv *bat_priv, bat_dbg(DBG_ROUTES, bat_priv, "Adding route towards: %pM (via %pM)\n", orig_node->orig, neigh_node->addr); - hna_global_add_orig(bat_priv, orig_node, - hna_buff, hna_buff_len); + tt_global_add_orig(bat_priv, orig_node, + tt_buff, tt_buff_len); /* route changed */ - } else { + } else if (neigh_node && curr_router) { bat_dbg(DBG_ROUTES, bat_priv, "Changing route towards: %pM " "(now via %pM - was via %pM)\n", @@ -133,10 +132,9 @@ static void update_route(struct bat_priv *bat_priv, neigh_node_free_ref(curr_router); } - void update_routes(struct bat_priv *bat_priv, struct orig_node *orig_node, - struct neigh_node *neigh_node, unsigned char *hna_buff, - int hna_buff_len) + struct neigh_node *neigh_node, const unsigned char *tt_buff, + int tt_buff_len) { struct neigh_node *router = NULL; @@ -147,10 +145,10 @@ void update_routes(struct bat_priv *bat_priv, struct orig_node *orig_node, if (router != neigh_node) update_route(bat_priv, orig_node, neigh_node, - hna_buff, hna_buff_len); - /* may be just HNA changed */ + tt_buff, tt_buff_len); + /* may be just TT changed */ else - update_HNA(bat_priv, orig_node, hna_buff, hna_buff_len); + update_TT(bat_priv, orig_node, tt_buff, tt_buff_len); out: if (router) @@ -169,65 +167,41 @@ static int is_bidirectional_neigh(struct orig_node *orig_node, uint8_t orig_eq_count, neigh_rq_count, tq_own; int tq_asym_penalty, ret = 0; - if (orig_node == orig_neigh_node) { - rcu_read_lock(); - hlist_for_each_entry_rcu(tmp_neigh_node, node, - &orig_node->neigh_list, list) { - - if (!compare_eth(tmp_neigh_node->addr, - orig_neigh_node->orig)) - continue; - - if (tmp_neigh_node->if_incoming != if_incoming) - continue; - - if (!atomic_inc_not_zero(&tmp_neigh_node->refcount)) - continue; - - neigh_node = tmp_neigh_node; - } - rcu_read_unlock(); + /* find corresponding one hop neighbor */ + rcu_read_lock(); + hlist_for_each_entry_rcu(tmp_neigh_node, node, + &orig_neigh_node->neigh_list, list) { - if (!neigh_node) - neigh_node = create_neighbor(orig_node, - orig_neigh_node, - orig_neigh_node->orig, - if_incoming); - if (!neigh_node) - goto out; + if (!compare_eth(tmp_neigh_node->addr, orig_neigh_node->orig)) + continue; - neigh_node->last_valid = jiffies; - } else { - /* find packet count of corresponding one hop neighbor */ - rcu_read_lock(); - hlist_for_each_entry_rcu(tmp_neigh_node, node, - &orig_neigh_node->neigh_list, list) { + if (tmp_neigh_node->if_incoming != if_incoming) + continue; - if (!compare_eth(tmp_neigh_node->addr, - orig_neigh_node->orig)) - continue; + if (!atomic_inc_not_zero(&tmp_neigh_node->refcount)) + continue; - if (tmp_neigh_node->if_incoming != if_incoming) - continue; + neigh_node = tmp_neigh_node; + break; + } + rcu_read_unlock(); - if (!atomic_inc_not_zero(&tmp_neigh_node->refcount)) - continue; + if (!neigh_node) + neigh_node = create_neighbor(orig_neigh_node, + orig_neigh_node, + orig_neigh_node->orig, + if_incoming); - neigh_node = tmp_neigh_node; - } - rcu_read_unlock(); + if (!neigh_node) + goto out; - if (!neigh_node) - neigh_node = create_neighbor(orig_neigh_node, - orig_neigh_node, - orig_neigh_node->orig, - if_incoming); - if (!neigh_node) - goto out; - } + /* if orig_node is direct neighbour update neigh_node last_valid */ + if (orig_node == orig_neigh_node) + neigh_node->last_valid = jiffies; orig_node->last_valid = jiffies; + /* find packet count of corresponding one hop neighbor */ spin_lock_bh(&orig_node->ogm_cnt_lock); orig_eq_count = orig_neigh_node->bcast_own_sum[if_incoming->if_num]; neigh_rq_count = neigh_node->real_packet_count; @@ -372,9 +346,9 @@ out: } /* copy primary address for bonding */ -static void bonding_save_primary(struct orig_node *orig_node, +static void bonding_save_primary(const struct orig_node *orig_node, struct orig_node *orig_neigh_node, - struct batman_packet *batman_packet) + const struct batman_packet *batman_packet) { if (!(batman_packet->flags & PRIMARIES_FIRST_HOP)) return; @@ -382,19 +356,18 @@ static void bonding_save_primary(struct orig_node *orig_node, memcpy(orig_neigh_node->primary_addr, orig_node->orig, ETH_ALEN); } -static void update_orig(struct bat_priv *bat_priv, - struct orig_node *orig_node, - struct ethhdr *ethhdr, - struct batman_packet *batman_packet, +static void update_orig(struct bat_priv *bat_priv, struct orig_node *orig_node, + const struct ethhdr *ethhdr, + const struct batman_packet *batman_packet, struct hard_iface *if_incoming, - unsigned char *hna_buff, int hna_buff_len, + const unsigned char *tt_buff, int tt_buff_len, char is_duplicate) { struct neigh_node *neigh_node = NULL, *tmp_neigh_node = NULL; struct neigh_node *router = NULL; struct orig_node *orig_node_tmp; struct hlist_node *node; - int tmp_hna_buff_len; + int tmp_tt_buff_len; uint8_t bcast_own_sum_orig, bcast_own_sum_neigh; bat_dbg(DBG_BATMAN, bat_priv, "update_originator(): " @@ -459,18 +432,18 @@ static void update_orig(struct bat_priv *bat_priv, bonding_candidate_add(orig_node, neigh_node); - tmp_hna_buff_len = (hna_buff_len > batman_packet->num_hna * ETH_ALEN ? - batman_packet->num_hna * ETH_ALEN : hna_buff_len); + tmp_tt_buff_len = (tt_buff_len > batman_packet->num_tt * ETH_ALEN ? + batman_packet->num_tt * ETH_ALEN : tt_buff_len); /* if this neighbor already is our next hop there is nothing * to change */ router = orig_node_get_router(orig_node); if (router == neigh_node) - goto update_hna; + goto update_tt; /* if this neighbor does not offer a better TQ we won't consider it */ if (router && (router->tq_avg > neigh_node->tq_avg)) - goto update_hna; + goto update_tt; /* if the TQ is the same and the link not more symetric we * won't consider it either */ @@ -488,16 +461,16 @@ static void update_orig(struct bat_priv *bat_priv, spin_unlock_bh(&orig_node_tmp->ogm_cnt_lock); if (bcast_own_sum_orig >= bcast_own_sum_neigh) - goto update_hna; + goto update_tt; } update_routes(bat_priv, orig_node, neigh_node, - hna_buff, tmp_hna_buff_len); + tt_buff, tmp_tt_buff_len); goto update_gw; -update_hna: +update_tt: update_routes(bat_priv, orig_node, router, - hna_buff, tmp_hna_buff_len); + tt_buff, tmp_tt_buff_len); update_gw: if (orig_node->gw_flags != batman_packet->gw_flags) @@ -555,9 +528,9 @@ static int window_protected(struct bat_priv *bat_priv, * -1 the packet is old and has been received while the seqno window * was protected. Caller should drop it. */ -static char count_real_packets(struct ethhdr *ethhdr, - struct batman_packet *batman_packet, - struct hard_iface *if_incoming) +static char count_real_packets(const struct ethhdr *ethhdr, + const struct batman_packet *batman_packet, + const struct hard_iface *if_incoming) { struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); struct orig_node *orig_node; @@ -619,9 +592,9 @@ out: return ret; } -void receive_bat_packet(struct ethhdr *ethhdr, +void receive_bat_packet(const struct ethhdr *ethhdr, struct batman_packet *batman_packet, - unsigned char *hna_buff, int hna_buff_len, + const unsigned char *tt_buff, int tt_buff_len, struct hard_iface *if_incoming) { struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); @@ -688,7 +661,7 @@ void receive_bat_packet(struct ethhdr *ethhdr, hard_iface->net_dev->dev_addr)) is_my_oldorig = 1; - if (compare_eth(ethhdr->h_source, broadcast_addr)) + if (is_broadcast_ether_addr(ethhdr->h_source)) is_broadcast = 1; } rcu_read_unlock(); @@ -725,17 +698,16 @@ void receive_bat_packet(struct ethhdr *ethhdr, /* neighbor has to indicate direct link and it has to * come via the corresponding interface */ - /* if received seqno equals last send seqno save new - * seqno for bidirectional check */ + /* save packet seqno for bidirectional check */ if (has_directlink_flag && compare_eth(if_incoming->net_dev->dev_addr, - batman_packet->orig) && - (batman_packet->seqno - if_incoming_seqno + 2 == 0)) { + batman_packet->orig)) { offset = if_incoming->if_num * NUM_WORDS; spin_lock_bh(&orig_neigh_node->ogm_cnt_lock); word = &(orig_neigh_node->bcast_own[offset]); - bit_mark(word, 0); + bit_mark(word, + if_incoming_seqno - batman_packet->seqno - 2); orig_neigh_node->bcast_own_sum[if_incoming->if_num] = bit_packet_count(word); spin_unlock_bh(&orig_neigh_node->ogm_cnt_lock); @@ -818,14 +790,14 @@ void receive_bat_packet(struct ethhdr *ethhdr, ((orig_node->last_real_seqno == batman_packet->seqno) && (orig_node->last_ttl - 3 <= batman_packet->ttl)))) update_orig(bat_priv, orig_node, ethhdr, batman_packet, - if_incoming, hna_buff, hna_buff_len, is_duplicate); + if_incoming, tt_buff, tt_buff_len, is_duplicate); /* is single hop (direct) neighbor */ if (is_single_hop_neigh) { /* mark direct link on incoming interface */ schedule_forward_packet(orig_node, ethhdr, batman_packet, - 1, hna_buff_len, if_incoming); + 1, tt_buff_len, if_incoming); bat_dbg(DBG_BATMAN, bat_priv, "Forwarding packet: " "rebroadcast neighbor packet with direct link flag\n"); @@ -848,7 +820,7 @@ void receive_bat_packet(struct ethhdr *ethhdr, bat_dbg(DBG_BATMAN, bat_priv, "Forwarding packet: rebroadcast originator packet\n"); schedule_forward_packet(orig_node, ethhdr, batman_packet, - 0, hna_buff_len, if_incoming); + 0, tt_buff_len, if_incoming); out_neigh: if ((orig_neigh_node) && (!is_single_hop_neigh)) @@ -904,6 +876,7 @@ int recv_bat_packet(struct sk_buff *skb, struct hard_iface *hard_iface) static int recv_my_icmp_packet(struct bat_priv *bat_priv, struct sk_buff *skb, size_t icmp_len) { + struct hard_iface *primary_if = NULL; struct orig_node *orig_node = NULL; struct neigh_node *router = NULL; struct icmp_packet_rr *icmp_packet; @@ -917,7 +890,8 @@ static int recv_my_icmp_packet(struct bat_priv *bat_priv, goto out; } - if (!bat_priv->primary_if) + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) goto out; /* answer echo request (ping) */ @@ -937,8 +911,7 @@ static int recv_my_icmp_packet(struct bat_priv *bat_priv, icmp_packet = (struct icmp_packet_rr *)skb->data; memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN); - memcpy(icmp_packet->orig, - bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); + memcpy(icmp_packet->orig, primary_if->net_dev->dev_addr, ETH_ALEN); icmp_packet->msg_type = ECHO_REPLY; icmp_packet->ttl = TTL; @@ -946,6 +919,8 @@ static int recv_my_icmp_packet(struct bat_priv *bat_priv, ret = NET_RX_SUCCESS; out: + if (primary_if) + hardif_free_ref(primary_if); if (router) neigh_node_free_ref(router); if (orig_node) @@ -956,6 +931,7 @@ out: static int recv_icmp_ttl_exceeded(struct bat_priv *bat_priv, struct sk_buff *skb) { + struct hard_iface *primary_if = NULL; struct orig_node *orig_node = NULL; struct neigh_node *router = NULL; struct icmp_packet *icmp_packet; @@ -971,7 +947,8 @@ static int recv_icmp_ttl_exceeded(struct bat_priv *bat_priv, goto out; } - if (!bat_priv->primary_if) + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) goto out; /* get routing information */ @@ -990,8 +967,7 @@ static int recv_icmp_ttl_exceeded(struct bat_priv *bat_priv, icmp_packet = (struct icmp_packet *)skb->data; memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN); - memcpy(icmp_packet->orig, - bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); + memcpy(icmp_packet->orig, primary_if->net_dev->dev_addr, ETH_ALEN); icmp_packet->msg_type = TTL_EXCEEDED; icmp_packet->ttl = TTL; @@ -999,6 +975,8 @@ static int recv_icmp_ttl_exceeded(struct bat_priv *bat_priv, ret = NET_RX_SUCCESS; out: + if (primary_if) + hardif_free_ref(primary_if); if (router) neigh_node_free_ref(router); if (orig_node) @@ -1095,7 +1073,7 @@ out: * This method rotates the bonding list and increases the * returned router's refcount. */ static struct neigh_node *find_bond_router(struct orig_node *primary_orig, - struct hard_iface *recv_if) + const struct hard_iface *recv_if) { struct neigh_node *tmp_neigh_node; struct neigh_node *router = NULL, *first_candidate = NULL; @@ -1146,7 +1124,7 @@ out: * * Increases the returned router's refcount */ static struct neigh_node *find_ifalter_router(struct orig_node *primary_orig, - struct hard_iface *recv_if) + const struct hard_iface *recv_if) { struct neigh_node *tmp_neigh_node; struct neigh_node *router = NULL, *first_candidate = NULL; @@ -1194,7 +1172,7 @@ static struct neigh_node *find_ifalter_router(struct orig_node *primary_orig, * refcount.*/ struct neigh_node *find_router(struct bat_priv *bat_priv, struct orig_node *orig_node, - struct hard_iface *recv_if) + const struct hard_iface *recv_if) { struct orig_node *primary_orig_node; struct orig_node *router_orig; @@ -1207,7 +1185,7 @@ struct neigh_node *find_router(struct bat_priv *bat_priv, router = orig_node_get_router(orig_node); if (!router) - return NULL; + goto err; /* without bonding, the first node should * always choose the default router. */ @@ -1216,10 +1194,8 @@ struct neigh_node *find_router(struct bat_priv *bat_priv, rcu_read_lock(); /* select default router to output */ router_orig = router->orig_node; - if (!router_orig) { - rcu_read_unlock(); - return NULL; - } + if (!router_orig) + goto err_unlock; if ((!recv_if) && (!bonding_enabled)) goto return_router; @@ -1260,8 +1236,17 @@ struct neigh_node *find_router(struct bat_priv *bat_priv, router = find_ifalter_router(primary_orig_node, recv_if); return_router: + if (router && router->if_incoming->if_status != IF_ACTIVE) + goto err_unlock; + rcu_read_unlock(); return router; +err_unlock: + rcu_read_unlock(); +err: + if (router) + neigh_node_free_ref(router); + return NULL; } static int check_unicast_packet(struct sk_buff *skb, int hdr_size) @@ -1310,13 +1295,10 @@ int route_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if) } /* get routing information */ - rcu_read_lock(); orig_node = orig_hash_find(bat_priv, unicast_packet->dest); if (!orig_node) - goto unlock; - - rcu_read_unlock(); + goto out; /* find_router() increases neigh_nodes refcount if found. */ neigh_node = find_router(bat_priv, orig_node, recv_if); @@ -1362,10 +1344,7 @@ int route_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if) /* route it */ send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); ret = NET_RX_SUCCESS; - goto out; -unlock: - rcu_read_unlock(); out: if (neigh_node) neigh_node_free_ref(neigh_node); @@ -1377,7 +1356,7 @@ out: int recv_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if) { struct unicast_packet *unicast_packet; - int hdr_size = sizeof(struct unicast_packet); + int hdr_size = sizeof(*unicast_packet); if (check_unicast_packet(skb, hdr_size) < 0) return NET_RX_DROP; @@ -1397,7 +1376,7 @@ int recv_ucast_frag_packet(struct sk_buff *skb, struct hard_iface *recv_if) { struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface); struct unicast_frag_packet *unicast_packet; - int hdr_size = sizeof(struct unicast_frag_packet); + int hdr_size = sizeof(*unicast_packet); struct sk_buff *new_skb = NULL; int ret; @@ -1433,7 +1412,7 @@ int recv_bcast_packet(struct sk_buff *skb, struct hard_iface *recv_if) struct orig_node *orig_node = NULL; struct bcast_packet *bcast_packet; struct ethhdr *ethhdr; - int hdr_size = sizeof(struct bcast_packet); + int hdr_size = sizeof(*bcast_packet); int ret = NET_RX_DROP; int32_t seq_diff; @@ -1464,13 +1443,10 @@ int recv_bcast_packet(struct sk_buff *skb, struct hard_iface *recv_if) if (bcast_packet->ttl < 2) goto out; - rcu_read_lock(); orig_node = orig_hash_find(bat_priv, bcast_packet->orig); if (!orig_node) - goto rcu_unlock; - - rcu_read_unlock(); + goto out; spin_lock_bh(&orig_node->bcast_seqno_lock); @@ -1501,9 +1477,6 @@ int recv_bcast_packet(struct sk_buff *skb, struct hard_iface *recv_if) ret = NET_RX_SUCCESS; goto out; -rcu_unlock: - rcu_read_unlock(); - goto out; spin_unlock: spin_unlock_bh(&orig_node->bcast_seqno_lock); out: @@ -1517,7 +1490,7 @@ int recv_vis_packet(struct sk_buff *skb, struct hard_iface *recv_if) struct vis_packet *vis_packet; struct ethhdr *ethhdr; struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface); - int hdr_size = sizeof(struct vis_packet); + int hdr_size = sizeof(*vis_packet); /* keep skb linear */ if (skb_linearize(skb) < 0) diff --git a/net/batman-adv/routing.h b/net/batman-adv/routing.h index b5a064c88a4..0ce03923ec0 100644 --- a/net/batman-adv/routing.h +++ b/net/batman-adv/routing.h @@ -23,13 +23,13 @@ #define _NET_BATMAN_ADV_ROUTING_H_ void slide_own_bcast_window(struct hard_iface *hard_iface); -void receive_bat_packet(struct ethhdr *ethhdr, - struct batman_packet *batman_packet, - unsigned char *hna_buff, int hna_buff_len, - struct hard_iface *if_incoming); +void receive_bat_packet(const struct ethhdr *ethhdr, + struct batman_packet *batman_packet, + const unsigned char *tt_buff, int tt_buff_len, + struct hard_iface *if_incoming); void update_routes(struct bat_priv *bat_priv, struct orig_node *orig_node, - struct neigh_node *neigh_node, unsigned char *hna_buff, - int hna_buff_len); + struct neigh_node *neigh_node, const unsigned char *tt_buff, + int tt_buff_len); int route_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if); int recv_icmp_packet(struct sk_buff *skb, struct hard_iface *recv_if); int recv_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if); @@ -39,7 +39,7 @@ int recv_vis_packet(struct sk_buff *skb, struct hard_iface *recv_if); int recv_bat_packet(struct sk_buff *skb, struct hard_iface *recv_if); struct neigh_node *find_router(struct bat_priv *bat_priv, struct orig_node *orig_node, - struct hard_iface *recv_if); + const struct hard_iface *recv_if); void bonding_candidate_del(struct orig_node *orig_node, struct neigh_node *neigh_node); diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index e78670c3c4b..a1b8c3173a3 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -33,14 +33,14 @@ static void send_outstanding_bcast_packet(struct work_struct *work); /* apply hop penalty for a normal link */ -static uint8_t hop_penalty(const uint8_t tq, struct bat_priv *bat_priv) +static uint8_t hop_penalty(uint8_t tq, const struct bat_priv *bat_priv) { int hop_penalty = atomic_read(&bat_priv->hop_penalty); return (tq * (TQ_MAX_VALUE - hop_penalty)) / (TQ_MAX_VALUE); } /* when do we schedule our own packet to be sent */ -static unsigned long own_send_time(struct bat_priv *bat_priv) +static unsigned long own_send_time(const struct bat_priv *bat_priv) { return jiffies + msecs_to_jiffies( atomic_read(&bat_priv->orig_interval) - @@ -55,9 +55,8 @@ static unsigned long forward_send_time(void) /* send out an already prepared packet to the given address via the * specified batman interface */ -int send_skb_packet(struct sk_buff *skb, - struct hard_iface *hard_iface, - uint8_t *dst_addr) +int send_skb_packet(struct sk_buff *skb, struct hard_iface *hard_iface, + const uint8_t *dst_addr) { struct ethhdr *ethhdr; @@ -74,7 +73,7 @@ int send_skb_packet(struct sk_buff *skb, } /* push to the ethernet header. */ - if (my_skb_head_push(skb, sizeof(struct ethhdr)) < 0) + if (my_skb_head_push(skb, sizeof(*ethhdr)) < 0) goto send_skb_err; skb_reset_mac_header(skb); @@ -121,7 +120,7 @@ static void send_packet_to_if(struct forw_packet *forw_packet, /* adjust all flags and log packets */ while (aggregated_packet(buff_pos, forw_packet->packet_len, - batman_packet->num_hna)) { + batman_packet->num_tt)) { /* we might have aggregated direct link packets with an * ordinary base packet */ @@ -145,8 +144,8 @@ static void send_packet_to_if(struct forw_packet *forw_packet, hard_iface->net_dev->name, hard_iface->net_dev->dev_addr); - buff_pos += sizeof(struct batman_packet) + - (batman_packet->num_hna * ETH_ALEN); + buff_pos += sizeof(*batman_packet) + + (batman_packet->num_tt * ETH_ALEN); packet_num++; batman_packet = (struct batman_packet *) (forw_packet->skb->data + buff_pos); @@ -221,19 +220,18 @@ static void rebuild_batman_packet(struct bat_priv *bat_priv, unsigned char *new_buff; struct batman_packet *batman_packet; - new_len = sizeof(struct batman_packet) + - (bat_priv->num_local_hna * ETH_ALEN); + new_len = sizeof(*batman_packet) + (bat_priv->num_local_tt * ETH_ALEN); new_buff = kmalloc(new_len, GFP_ATOMIC); /* keep old buffer if kmalloc should fail */ if (new_buff) { memcpy(new_buff, hard_iface->packet_buff, - sizeof(struct batman_packet)); + sizeof(*batman_packet)); batman_packet = (struct batman_packet *)new_buff; - batman_packet->num_hna = hna_local_fill_buffer(bat_priv, - new_buff + sizeof(struct batman_packet), - new_len - sizeof(struct batman_packet)); + batman_packet->num_tt = tt_local_fill_buffer(bat_priv, + new_buff + sizeof(*batman_packet), + new_len - sizeof(*batman_packet)); kfree(hard_iface->packet_buff); hard_iface->packet_buff = new_buff; @@ -244,6 +242,7 @@ static void rebuild_batman_packet(struct bat_priv *bat_priv, void schedule_own_packet(struct hard_iface *hard_iface) { struct bat_priv *bat_priv = netdev_priv(hard_iface->soft_iface); + struct hard_iface *primary_if; unsigned long send_time; struct batman_packet *batman_packet; int vis_server; @@ -253,6 +252,7 @@ void schedule_own_packet(struct hard_iface *hard_iface) return; vis_server = atomic_read(&bat_priv->vis_mode); + primary_if = primary_if_get_selected(bat_priv); /** * the interface gets activated here to avoid race conditions between @@ -264,9 +264,9 @@ void schedule_own_packet(struct hard_iface *hard_iface) if (hard_iface->if_status == IF_TO_BE_ACTIVATED) hard_iface->if_status = IF_ACTIVE; - /* if local hna has changed and interface is a primary interface */ - if ((atomic_read(&bat_priv->hna_local_changed)) && - (hard_iface == bat_priv->primary_if)) + /* if local tt has changed and interface is a primary interface */ + if ((atomic_read(&bat_priv->tt_local_changed)) && + (hard_iface == primary_if)) rebuild_batman_packet(bat_priv, hard_iface); /** @@ -284,12 +284,12 @@ void schedule_own_packet(struct hard_iface *hard_iface) else batman_packet->flags &= ~VIS_SERVER; - if ((hard_iface == bat_priv->primary_if) && + if ((hard_iface == primary_if) && (atomic_read(&bat_priv->gw_mode) == GW_MODE_SERVER)) batman_packet->gw_flags = (uint8_t)atomic_read(&bat_priv->gw_bandwidth); else - batman_packet->gw_flags = 0; + batman_packet->gw_flags = NO_FLAGS; atomic_inc(&hard_iface->seqno); @@ -299,12 +299,15 @@ void schedule_own_packet(struct hard_iface *hard_iface) hard_iface->packet_buff, hard_iface->packet_len, hard_iface, 1, send_time); + + if (primary_if) + hardif_free_ref(primary_if); } void schedule_forward_packet(struct orig_node *orig_node, - struct ethhdr *ethhdr, + const struct ethhdr *ethhdr, struct batman_packet *batman_packet, - uint8_t directlink, int hna_buff_len, + uint8_t directlink, int tt_buff_len, struct hard_iface *if_incoming) { struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); @@ -364,7 +367,7 @@ void schedule_forward_packet(struct orig_node *orig_node, send_time = forward_send_time(); add_bat_packet_to_list(bat_priv, (unsigned char *)batman_packet, - sizeof(struct batman_packet) + hna_buff_len, + sizeof(*batman_packet) + tt_buff_len, if_incoming, 0, send_time); } @@ -372,6 +375,8 @@ static void forw_packet_free(struct forw_packet *forw_packet) { if (forw_packet->skb) kfree_skb(forw_packet->skb); + if (forw_packet->if_incoming) + hardif_free_ref(forw_packet->if_incoming); kfree(forw_packet); } @@ -393,7 +398,6 @@ static void _add_bcast_packet_to_list(struct bat_priv *bat_priv, send_time); } -#define atomic_dec_not_zero(v) atomic_add_unless((v), -1, 0) /* add a broadcast packet to the queue and setup timers. broadcast packets * are sent multiple times to increase probability for beeing received. * @@ -402,36 +406,40 @@ static void _add_bcast_packet_to_list(struct bat_priv *bat_priv, * * The skb is not consumed, so the caller should make sure that the * skb is freed. */ -int add_bcast_packet_to_list(struct bat_priv *bat_priv, struct sk_buff *skb) +int add_bcast_packet_to_list(struct bat_priv *bat_priv, + const struct sk_buff *skb) { + struct hard_iface *primary_if = NULL; struct forw_packet *forw_packet; struct bcast_packet *bcast_packet; + struct sk_buff *newskb; if (!atomic_dec_not_zero(&bat_priv->bcast_queue_left)) { bat_dbg(DBG_BATMAN, bat_priv, "bcast packet queue full\n"); goto out; } - if (!bat_priv->primary_if) - goto out; + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) + goto out_and_inc; - forw_packet = kmalloc(sizeof(struct forw_packet), GFP_ATOMIC); + forw_packet = kmalloc(sizeof(*forw_packet), GFP_ATOMIC); if (!forw_packet) goto out_and_inc; - skb = skb_copy(skb, GFP_ATOMIC); - if (!skb) + newskb = skb_copy(skb, GFP_ATOMIC); + if (!newskb) goto packet_free; /* as we have a copy now, it is safe to decrease the TTL */ - bcast_packet = (struct bcast_packet *)skb->data; + bcast_packet = (struct bcast_packet *)newskb->data; bcast_packet->ttl--; - skb_reset_mac_header(skb); + skb_reset_mac_header(newskb); - forw_packet->skb = skb; - forw_packet->if_incoming = bat_priv->primary_if; + forw_packet->skb = newskb; + forw_packet->if_incoming = primary_if; /* how often did we send the bcast packet ? */ forw_packet->num_packets = 0; @@ -444,6 +452,8 @@ packet_free: out_and_inc: atomic_inc(&bat_priv->bcast_queue_left); out: + if (primary_if) + hardif_free_ref(primary_if); return NETDEV_TX_BUSY; } @@ -527,10 +537,11 @@ out: } void purge_outstanding_packets(struct bat_priv *bat_priv, - struct hard_iface *hard_iface) + const struct hard_iface *hard_iface) { struct forw_packet *forw_packet; struct hlist_node *tmp_node, *safe_tmp_node; + bool pending; if (hard_iface) bat_dbg(DBG_BATMAN, bat_priv, @@ -559,8 +570,13 @@ void purge_outstanding_packets(struct bat_priv *bat_priv, * send_outstanding_bcast_packet() will lock the list to * delete the item from the list */ - cancel_delayed_work_sync(&forw_packet->delayed_work); + pending = cancel_delayed_work_sync(&forw_packet->delayed_work); spin_lock_bh(&bat_priv->forw_bcast_list_lock); + + if (pending) { + hlist_del(&forw_packet->list); + forw_packet_free(forw_packet); + } } spin_unlock_bh(&bat_priv->forw_bcast_list_lock); @@ -583,8 +599,13 @@ void purge_outstanding_packets(struct bat_priv *bat_priv, * send_outstanding_bat_packet() will lock the list to * delete the item from the list */ - cancel_delayed_work_sync(&forw_packet->delayed_work); + pending = cancel_delayed_work_sync(&forw_packet->delayed_work); spin_lock_bh(&bat_priv->forw_bat_list_lock); + + if (pending) { + hlist_del(&forw_packet->list); + forw_packet_free(forw_packet); + } } spin_unlock_bh(&bat_priv->forw_bat_list_lock); } diff --git a/net/batman-adv/send.h b/net/batman-adv/send.h index 7b2ff19c05e..eceab870024 100644 --- a/net/batman-adv/send.h +++ b/net/batman-adv/send.h @@ -22,18 +22,18 @@ #ifndef _NET_BATMAN_ADV_SEND_H_ #define _NET_BATMAN_ADV_SEND_H_ -int send_skb_packet(struct sk_buff *skb, - struct hard_iface *hard_iface, - uint8_t *dst_addr); +int send_skb_packet(struct sk_buff *skb, struct hard_iface *hard_iface, + const uint8_t *dst_addr); void schedule_own_packet(struct hard_iface *hard_iface); void schedule_forward_packet(struct orig_node *orig_node, - struct ethhdr *ethhdr, + const struct ethhdr *ethhdr, struct batman_packet *batman_packet, - uint8_t directlink, int hna_buff_len, + uint8_t directlink, int tt_buff_len, struct hard_iface *if_outgoing); -int add_bcast_packet_to_list(struct bat_priv *bat_priv, struct sk_buff *skb); +int add_bcast_packet_to_list(struct bat_priv *bat_priv, + const struct sk_buff *skb); void send_outstanding_bat_packet(struct work_struct *work); void purge_outstanding_packets(struct bat_priv *bat_priv, - struct hard_iface *hard_iface); + const struct hard_iface *hard_iface); #endif /* _NET_BATMAN_ADV_SEND_H_ */ diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 1f6f756bc58..b8d3f248efd 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -43,8 +43,6 @@ static void bat_get_drvinfo(struct net_device *dev, static u32 bat_get_msglevel(struct net_device *dev); static void bat_set_msglevel(struct net_device *dev, u32 value); static u32 bat_get_link(struct net_device *dev); -static u32 bat_get_rx_csum(struct net_device *dev); -static int bat_set_rx_csum(struct net_device *dev, u32 data); static const struct ethtool_ops bat_ethtool_ops = { .get_settings = bat_get_settings, @@ -52,8 +50,6 @@ static const struct ethtool_ops bat_ethtool_ops = { .get_msglevel = bat_get_msglevel, .set_msglevel = bat_set_msglevel, .get_link = bat_get_link, - .get_rx_csum = bat_get_rx_csum, - .set_rx_csum = bat_set_rx_csum }; int my_skb_head_push(struct sk_buff *skb, unsigned int len) @@ -76,169 +72,371 @@ int my_skb_head_push(struct sk_buff *skb, unsigned int len) return 0; } -static void softif_neigh_free_rcu(struct rcu_head *rcu) -{ - struct softif_neigh *softif_neigh; - - softif_neigh = container_of(rcu, struct softif_neigh, rcu); - kfree(softif_neigh); -} - static void softif_neigh_free_ref(struct softif_neigh *softif_neigh) { if (atomic_dec_and_test(&softif_neigh->refcount)) - call_rcu(&softif_neigh->rcu, softif_neigh_free_rcu); + kfree_rcu(softif_neigh, rcu); } -static struct softif_neigh *softif_neigh_get_selected(struct bat_priv *bat_priv) +static void softif_neigh_vid_free_rcu(struct rcu_head *rcu) { - struct softif_neigh *neigh; - - rcu_read_lock(); - neigh = rcu_dereference(bat_priv->softif_neigh); - - if (neigh && !atomic_inc_not_zero(&neigh->refcount)) - neigh = NULL; - - rcu_read_unlock(); - return neigh; -} + struct softif_neigh_vid *softif_neigh_vid; + struct softif_neigh *softif_neigh; + struct hlist_node *node, *node_tmp; + struct bat_priv *bat_priv; -static void softif_neigh_select(struct bat_priv *bat_priv, - struct softif_neigh *new_neigh) -{ - struct softif_neigh *curr_neigh; + softif_neigh_vid = container_of(rcu, struct softif_neigh_vid, rcu); + bat_priv = softif_neigh_vid->bat_priv; spin_lock_bh(&bat_priv->softif_neigh_lock); - - if (new_neigh && !atomic_inc_not_zero(&new_neigh->refcount)) - new_neigh = NULL; - - curr_neigh = bat_priv->softif_neigh; - rcu_assign_pointer(bat_priv->softif_neigh, new_neigh); - - if (curr_neigh) - softif_neigh_free_ref(curr_neigh); - + hlist_for_each_entry_safe(softif_neigh, node, node_tmp, + &softif_neigh_vid->softif_neigh_list, list) { + hlist_del_rcu(&softif_neigh->list); + softif_neigh_free_ref(softif_neigh); + } spin_unlock_bh(&bat_priv->softif_neigh_lock); + + kfree(softif_neigh_vid); } -static void softif_neigh_deselect(struct bat_priv *bat_priv) +static void softif_neigh_vid_free_ref(struct softif_neigh_vid *softif_neigh_vid) { - softif_neigh_select(bat_priv, NULL); + if (atomic_dec_and_test(&softif_neigh_vid->refcount)) + call_rcu(&softif_neigh_vid->rcu, softif_neigh_vid_free_rcu); } -void softif_neigh_purge(struct bat_priv *bat_priv) +static struct softif_neigh_vid *softif_neigh_vid_get(struct bat_priv *bat_priv, + short vid) { - struct softif_neigh *softif_neigh, *curr_softif_neigh; - struct hlist_node *node, *node_tmp; - char do_deselect = 0; - - curr_softif_neigh = softif_neigh_get_selected(bat_priv); - - spin_lock_bh(&bat_priv->softif_neigh_lock); - - hlist_for_each_entry_safe(softif_neigh, node, node_tmp, - &bat_priv->softif_neigh_list, list) { + struct softif_neigh_vid *softif_neigh_vid; + struct hlist_node *node; - if ((!time_after(jiffies, softif_neigh->last_seen + - msecs_to_jiffies(SOFTIF_NEIGH_TIMEOUT))) && - (atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE)) + rcu_read_lock(); + hlist_for_each_entry_rcu(softif_neigh_vid, node, + &bat_priv->softif_neigh_vids, list) { + if (softif_neigh_vid->vid != vid) continue; - if (curr_softif_neigh == softif_neigh) { - bat_dbg(DBG_ROUTES, bat_priv, - "Current mesh exit point '%pM' vanished " - "(vid: %d).\n", - softif_neigh->addr, softif_neigh->vid); - do_deselect = 1; - } + if (!atomic_inc_not_zero(&softif_neigh_vid->refcount)) + continue; - hlist_del_rcu(&softif_neigh->list); - softif_neigh_free_ref(softif_neigh); + goto out; } - spin_unlock_bh(&bat_priv->softif_neigh_lock); + softif_neigh_vid = kzalloc(sizeof(*softif_neigh_vid), GFP_ATOMIC); + if (!softif_neigh_vid) + goto out; - /* soft_neigh_deselect() needs to acquire the softif_neigh_lock */ - if (do_deselect) - softif_neigh_deselect(bat_priv); + softif_neigh_vid->vid = vid; + softif_neigh_vid->bat_priv = bat_priv; - if (curr_softif_neigh) - softif_neigh_free_ref(curr_softif_neigh); + /* initialize with 2 - caller decrements counter by one */ + atomic_set(&softif_neigh_vid->refcount, 2); + INIT_HLIST_HEAD(&softif_neigh_vid->softif_neigh_list); + INIT_HLIST_NODE(&softif_neigh_vid->list); + spin_lock_bh(&bat_priv->softif_neigh_vid_lock); + hlist_add_head_rcu(&softif_neigh_vid->list, + &bat_priv->softif_neigh_vids); + spin_unlock_bh(&bat_priv->softif_neigh_vid_lock); + +out: + rcu_read_unlock(); + return softif_neigh_vid; } static struct softif_neigh *softif_neigh_get(struct bat_priv *bat_priv, - uint8_t *addr, short vid) + const uint8_t *addr, short vid) { - struct softif_neigh *softif_neigh; + struct softif_neigh_vid *softif_neigh_vid; + struct softif_neigh *softif_neigh = NULL; struct hlist_node *node; + softif_neigh_vid = softif_neigh_vid_get(bat_priv, vid); + if (!softif_neigh_vid) + goto out; + rcu_read_lock(); hlist_for_each_entry_rcu(softif_neigh, node, - &bat_priv->softif_neigh_list, list) { + &softif_neigh_vid->softif_neigh_list, + list) { if (!compare_eth(softif_neigh->addr, addr)) continue; - if (softif_neigh->vid != vid) - continue; - if (!atomic_inc_not_zero(&softif_neigh->refcount)) continue; softif_neigh->last_seen = jiffies; - goto out; + goto unlock; } - softif_neigh = kzalloc(sizeof(struct softif_neigh), GFP_ATOMIC); + softif_neigh = kzalloc(sizeof(*softif_neigh), GFP_ATOMIC); if (!softif_neigh) - goto out; + goto unlock; memcpy(softif_neigh->addr, addr, ETH_ALEN); - softif_neigh->vid = vid; softif_neigh->last_seen = jiffies; /* initialize with 2 - caller decrements counter by one */ atomic_set(&softif_neigh->refcount, 2); INIT_HLIST_NODE(&softif_neigh->list); spin_lock_bh(&bat_priv->softif_neigh_lock); - hlist_add_head_rcu(&softif_neigh->list, &bat_priv->softif_neigh_list); + hlist_add_head_rcu(&softif_neigh->list, + &softif_neigh_vid->softif_neigh_list); spin_unlock_bh(&bat_priv->softif_neigh_lock); +unlock: + rcu_read_unlock(); out: + if (softif_neigh_vid) + softif_neigh_vid_free_ref(softif_neigh_vid); + return softif_neigh; +} + +static struct softif_neigh *softif_neigh_get_selected( + struct softif_neigh_vid *softif_neigh_vid) +{ + struct softif_neigh *softif_neigh; + + rcu_read_lock(); + softif_neigh = rcu_dereference(softif_neigh_vid->softif_neigh); + + if (softif_neigh && !atomic_inc_not_zero(&softif_neigh->refcount)) + softif_neigh = NULL; + rcu_read_unlock(); return softif_neigh; } +static struct softif_neigh *softif_neigh_vid_get_selected( + struct bat_priv *bat_priv, + short vid) +{ + struct softif_neigh_vid *softif_neigh_vid; + struct softif_neigh *softif_neigh = NULL; + + softif_neigh_vid = softif_neigh_vid_get(bat_priv, vid); + if (!softif_neigh_vid) + goto out; + + softif_neigh = softif_neigh_get_selected(softif_neigh_vid); +out: + if (softif_neigh_vid) + softif_neigh_vid_free_ref(softif_neigh_vid); + return softif_neigh; +} + +static void softif_neigh_vid_select(struct bat_priv *bat_priv, + struct softif_neigh *new_neigh, + short vid) +{ + struct softif_neigh_vid *softif_neigh_vid; + struct softif_neigh *curr_neigh; + + softif_neigh_vid = softif_neigh_vid_get(bat_priv, vid); + if (!softif_neigh_vid) + goto out; + + spin_lock_bh(&bat_priv->softif_neigh_lock); + + if (new_neigh && !atomic_inc_not_zero(&new_neigh->refcount)) + new_neigh = NULL; + + curr_neigh = rcu_dereference_protected(softif_neigh_vid->softif_neigh, + 1); + rcu_assign_pointer(softif_neigh_vid->softif_neigh, new_neigh); + + if ((curr_neigh) && (!new_neigh)) + bat_dbg(DBG_ROUTES, bat_priv, + "Removing mesh exit point on vid: %d (prev: %pM).\n", + vid, curr_neigh->addr); + else if ((curr_neigh) && (new_neigh)) + bat_dbg(DBG_ROUTES, bat_priv, + "Changing mesh exit point on vid: %d from %pM " + "to %pM.\n", vid, curr_neigh->addr, new_neigh->addr); + else if ((!curr_neigh) && (new_neigh)) + bat_dbg(DBG_ROUTES, bat_priv, + "Setting mesh exit point on vid: %d to %pM.\n", + vid, new_neigh->addr); + + if (curr_neigh) + softif_neigh_free_ref(curr_neigh); + + spin_unlock_bh(&bat_priv->softif_neigh_lock); + +out: + if (softif_neigh_vid) + softif_neigh_vid_free_ref(softif_neigh_vid); +} + +static void softif_neigh_vid_deselect(struct bat_priv *bat_priv, + struct softif_neigh_vid *softif_neigh_vid) +{ + struct softif_neigh *curr_neigh; + struct softif_neigh *softif_neigh = NULL, *softif_neigh_tmp; + struct hard_iface *primary_if = NULL; + struct hlist_node *node; + + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; + + /* find new softif_neigh immediately to avoid temporary loops */ + rcu_read_lock(); + curr_neigh = rcu_dereference(softif_neigh_vid->softif_neigh); + + hlist_for_each_entry_rcu(softif_neigh_tmp, node, + &softif_neigh_vid->softif_neigh_list, + list) { + if (softif_neigh_tmp == curr_neigh) + continue; + + /* we got a neighbor but its mac is 'bigger' than ours */ + if (memcmp(primary_if->net_dev->dev_addr, + softif_neigh_tmp->addr, ETH_ALEN) < 0) + continue; + + if (!atomic_inc_not_zero(&softif_neigh_tmp->refcount)) + continue; + + softif_neigh = softif_neigh_tmp; + goto unlock; + } + +unlock: + rcu_read_unlock(); +out: + softif_neigh_vid_select(bat_priv, softif_neigh, softif_neigh_vid->vid); + + if (primary_if) + hardif_free_ref(primary_if); + if (softif_neigh) + softif_neigh_free_ref(softif_neigh); +} + int softif_neigh_seq_print_text(struct seq_file *seq, void *offset) { struct net_device *net_dev = (struct net_device *)seq->private; struct bat_priv *bat_priv = netdev_priv(net_dev); + struct softif_neigh_vid *softif_neigh_vid; struct softif_neigh *softif_neigh; - struct hlist_node *node; + struct hard_iface *primary_if; + struct hlist_node *node, *node_tmp; struct softif_neigh *curr_softif_neigh; + int ret = 0, last_seen_secs, last_seen_msecs; - if (!bat_priv->primary_if) { - return seq_printf(seq, "BATMAN mesh %s disabled - " - "please specify interfaces to enable it\n", - net_dev->name); + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) { + ret = seq_printf(seq, "BATMAN mesh %s disabled - " + "please specify interfaces to enable it\n", + net_dev->name); + goto out; + } + + if (primary_if->if_status != IF_ACTIVE) { + ret = seq_printf(seq, "BATMAN mesh %s " + "disabled - primary interface not active\n", + net_dev->name); + goto out; } seq_printf(seq, "Softif neighbor list (%s)\n", net_dev->name); - curr_softif_neigh = softif_neigh_get_selected(bat_priv); rcu_read_lock(); - hlist_for_each_entry_rcu(softif_neigh, node, - &bat_priv->softif_neigh_list, list) - seq_printf(seq, "%s %pM (vid: %d)\n", - curr_softif_neigh == softif_neigh - ? "=>" : " ", softif_neigh->addr, - softif_neigh->vid); + hlist_for_each_entry_rcu(softif_neigh_vid, node, + &bat_priv->softif_neigh_vids, list) { + seq_printf(seq, " %-15s %s on vid: %d\n", + "Originator", "last-seen", softif_neigh_vid->vid); + + curr_softif_neigh = softif_neigh_get_selected(softif_neigh_vid); + + hlist_for_each_entry_rcu(softif_neigh, node_tmp, + &softif_neigh_vid->softif_neigh_list, + list) { + last_seen_secs = jiffies_to_msecs(jiffies - + softif_neigh->last_seen) / 1000; + last_seen_msecs = jiffies_to_msecs(jiffies - + softif_neigh->last_seen) % 1000; + seq_printf(seq, "%s %pM %3i.%03is\n", + curr_softif_neigh == softif_neigh + ? "=>" : " ", softif_neigh->addr, + last_seen_secs, last_seen_msecs); + } + + if (curr_softif_neigh) + softif_neigh_free_ref(curr_softif_neigh); + + seq_printf(seq, "\n"); + } rcu_read_unlock(); - if (curr_softif_neigh) - softif_neigh_free_ref(curr_softif_neigh); - return 0; +out: + if (primary_if) + hardif_free_ref(primary_if); + return ret; +} + +void softif_neigh_purge(struct bat_priv *bat_priv) +{ + struct softif_neigh *softif_neigh, *curr_softif_neigh; + struct softif_neigh_vid *softif_neigh_vid; + struct hlist_node *node, *node_tmp, *node_tmp2; + char do_deselect; + + rcu_read_lock(); + hlist_for_each_entry_rcu(softif_neigh_vid, node, + &bat_priv->softif_neigh_vids, list) { + if (!atomic_inc_not_zero(&softif_neigh_vid->refcount)) + continue; + + curr_softif_neigh = softif_neigh_get_selected(softif_neigh_vid); + do_deselect = 0; + + spin_lock_bh(&bat_priv->softif_neigh_lock); + hlist_for_each_entry_safe(softif_neigh, node_tmp, node_tmp2, + &softif_neigh_vid->softif_neigh_list, + list) { + if ((!time_after(jiffies, softif_neigh->last_seen + + msecs_to_jiffies(SOFTIF_NEIGH_TIMEOUT))) && + (atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE)) + continue; + + if (curr_softif_neigh == softif_neigh) { + bat_dbg(DBG_ROUTES, bat_priv, + "Current mesh exit point on vid: %d " + "'%pM' vanished.\n", + softif_neigh_vid->vid, + softif_neigh->addr); + do_deselect = 1; + } + + hlist_del_rcu(&softif_neigh->list); + softif_neigh_free_ref(softif_neigh); + } + spin_unlock_bh(&bat_priv->softif_neigh_lock); + + /* soft_neigh_vid_deselect() needs to acquire the + * softif_neigh_lock */ + if (do_deselect) + softif_neigh_vid_deselect(bat_priv, softif_neigh_vid); + + if (curr_softif_neigh) + softif_neigh_free_ref(curr_softif_neigh); + + softif_neigh_vid_free_ref(softif_neigh_vid); + } + rcu_read_unlock(); + + spin_lock_bh(&bat_priv->softif_neigh_vid_lock); + hlist_for_each_entry_safe(softif_neigh_vid, node, node_tmp, + &bat_priv->softif_neigh_vids, list) { + if (!hlist_empty(&softif_neigh_vid->softif_neigh_list)) + continue; + + hlist_del_rcu(&softif_neigh_vid->list); + softif_neigh_vid_free_ref(softif_neigh_vid); + } + spin_unlock_bh(&bat_priv->softif_neigh_vid_lock); + } static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev, @@ -247,7 +445,8 @@ static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev, struct bat_priv *bat_priv = netdev_priv(dev); struct ethhdr *ethhdr = (struct ethhdr *)skb->data; struct batman_packet *batman_packet; - struct softif_neigh *softif_neigh; + struct softif_neigh *softif_neigh = NULL; + struct hard_iface *primary_if = NULL; struct softif_neigh *curr_softif_neigh = NULL; if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) @@ -257,65 +456,52 @@ static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev, batman_packet = (struct batman_packet *)(skb->data + ETH_HLEN); if (batman_packet->version != COMPAT_VERSION) - goto err; + goto out; if (batman_packet->packet_type != BAT_PACKET) - goto err; + goto out; if (!(batman_packet->flags & PRIMARIES_FIRST_HOP)) - goto err; + goto out; if (is_my_mac(batman_packet->orig)) - goto err; + goto out; softif_neigh = softif_neigh_get(bat_priv, batman_packet->orig, vid); - if (!softif_neigh) - goto err; + goto out; - curr_softif_neigh = softif_neigh_get_selected(bat_priv); + curr_softif_neigh = softif_neigh_vid_get_selected(bat_priv, vid); if (curr_softif_neigh == softif_neigh) goto out; - /* we got a neighbor but its mac is 'bigger' than ours */ - if (memcmp(bat_priv->primary_if->net_dev->dev_addr, - softif_neigh->addr, ETH_ALEN) < 0) + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) goto out; - /* switch to new 'smallest neighbor' */ - if ((curr_softif_neigh) && - (memcmp(softif_neigh->addr, curr_softif_neigh->addr, - ETH_ALEN) < 0)) { - bat_dbg(DBG_ROUTES, bat_priv, - "Changing mesh exit point from %pM (vid: %d) " - "to %pM (vid: %d).\n", - curr_softif_neigh->addr, - curr_softif_neigh->vid, - softif_neigh->addr, softif_neigh->vid); - - softif_neigh_select(bat_priv, softif_neigh); + /* we got a neighbor but its mac is 'bigger' than ours */ + if (memcmp(primary_if->net_dev->dev_addr, + softif_neigh->addr, ETH_ALEN) < 0) goto out; - } /* close own batX device and use softif_neigh as exit node */ - if ((!curr_softif_neigh) && - (memcmp(softif_neigh->addr, - bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN) < 0)) { - bat_dbg(DBG_ROUTES, bat_priv, - "Setting mesh exit point to %pM (vid: %d).\n", - softif_neigh->addr, softif_neigh->vid); - - softif_neigh_select(bat_priv, softif_neigh); + if (!curr_softif_neigh) { + softif_neigh_vid_select(bat_priv, softif_neigh, vid); goto out; } + /* switch to new 'smallest neighbor' */ + if (memcmp(softif_neigh->addr, curr_softif_neigh->addr, ETH_ALEN) < 0) + softif_neigh_vid_select(bat_priv, softif_neigh, vid); + out: - softif_neigh_free_ref(softif_neigh); -err: kfree_skb(skb); + if (softif_neigh) + softif_neigh_free_ref(softif_neigh); if (curr_softif_neigh) softif_neigh_free_ref(curr_softif_neigh); - + if (primary_if) + hardif_free_ref(primary_if); return; } @@ -345,11 +531,11 @@ static int interface_set_mac_addr(struct net_device *dev, void *p) if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; - /* only modify hna-table if it has been initialised before */ + /* only modify transtable if it has been initialised before */ if (atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE) { - hna_local_remove(bat_priv, dev->dev_addr, + tt_local_remove(bat_priv, dev->dev_addr, "mac address changed"); - hna_local_add(dev, addr->sa_data); + tt_local_add(dev, addr->sa_data); } memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); @@ -371,6 +557,7 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) { struct ethhdr *ethhdr = (struct ethhdr *)skb->data; struct bat_priv *bat_priv = netdev_priv(soft_iface); + struct hard_iface *primary_if = NULL; struct bcast_packet *bcast_packet; struct vlan_ethhdr *vhdr; struct softif_neigh *curr_softif_neigh = NULL; @@ -401,12 +588,12 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) * if we have a another chosen mesh exit node in range * it will transport the packets to the mesh */ - curr_softif_neigh = softif_neigh_get_selected(bat_priv); - if ((curr_softif_neigh) && (curr_softif_neigh->vid == vid)) + curr_softif_neigh = softif_neigh_vid_get_selected(bat_priv, vid); + if (curr_softif_neigh) goto dropped; /* TODO: check this for locks */ - hna_local_add(soft_iface, ethhdr->h_source); + tt_local_add(soft_iface, ethhdr->h_source); if (is_multicast_ether_addr(ethhdr->h_dest)) { ret = gw_is_target(bat_priv, skb); @@ -420,10 +607,11 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) /* ethernet packet should be broadcasted */ if (do_bcast) { - if (!bat_priv->primary_if) + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) goto dropped; - if (my_skb_head_push(skb, sizeof(struct bcast_packet)) < 0) + if (my_skb_head_push(skb, sizeof(*bcast_packet)) < 0) goto dropped; bcast_packet = (struct bcast_packet *)skb->data; @@ -436,7 +624,7 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) /* hw address of first interface is the orig mac because only * this mac is known throughout the mesh */ memcpy(bcast_packet->orig, - bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); + primary_if->net_dev->dev_addr, ETH_ALEN); /* set broadcast sequence number */ bcast_packet->seqno = @@ -466,6 +654,8 @@ dropped_freed: end: if (curr_softif_neigh) softif_neigh_free_ref(curr_softif_neigh); + if (primary_if) + hardif_free_ref(primary_if); return NETDEV_TX_OK; } @@ -507,8 +697,8 @@ void interface_rx(struct net_device *soft_iface, * if we have a another chosen mesh exit node in range * it will transport the packets to the non-mesh network */ - curr_softif_neigh = softif_neigh_get_selected(bat_priv); - if (curr_softif_neigh && (curr_softif_neigh->vid == vid)) { + curr_softif_neigh = softif_neigh_vid_get_selected(bat_priv, vid); + if (curr_softif_neigh) { skb_push(skb, hdr_size); unicast_packet = (struct unicast_packet *)skb->data; @@ -591,8 +781,8 @@ static void interface_setup(struct net_device *dev) * have not been initialized yet */ dev->mtu = ETH_DATA_LEN; - dev->hard_header_len = BAT_HEADER_LEN; /* reserve more space in the - * skbuff for our header */ + /* reserve more space in the skbuff for our header */ + dev->hard_header_len = BAT_HEADER_LEN; /* generate random address */ random_ether_addr(dev_addr); @@ -600,24 +790,23 @@ static void interface_setup(struct net_device *dev) SET_ETHTOOL_OPS(dev, &bat_ethtool_ops); - memset(priv, 0, sizeof(struct bat_priv)); + memset(priv, 0, sizeof(*priv)); } -struct net_device *softif_create(char *name) +struct net_device *softif_create(const char *name) { struct net_device *soft_iface; struct bat_priv *bat_priv; int ret; - soft_iface = alloc_netdev(sizeof(struct bat_priv) , name, - interface_setup); + soft_iface = alloc_netdev(sizeof(*bat_priv), name, interface_setup); if (!soft_iface) { pr_err("Unable to allocate the batman interface: %s\n", name); goto out; } - ret = register_netdev(soft_iface); + ret = register_netdevice(soft_iface); if (ret < 0) { pr_err("Unable to register the batman interface '%s': %i\n", name, ret); @@ -641,11 +830,10 @@ struct net_device *softif_create(char *name) atomic_set(&bat_priv->mesh_state, MESH_INACTIVE); atomic_set(&bat_priv->bcast_seqno, 1); - atomic_set(&bat_priv->hna_local_changed, 0); + atomic_set(&bat_priv->tt_local_changed, 0); bat_priv->primary_if = NULL; bat_priv->num_ifaces = 0; - bat_priv->softif_neigh = NULL; ret = sysfs_add_meshif(soft_iface); if (ret < 0) @@ -683,7 +871,7 @@ void softif_destroy(struct net_device *soft_iface) unregister_netdevice(soft_iface); } -int softif_is_valid(struct net_device *net_dev) +int softif_is_valid(const struct net_device *net_dev) { #ifdef HAVE_NET_DEVICE_OPS if (net_dev->netdev_ops->ndo_start_xmit == interface_tx) @@ -701,7 +889,7 @@ static int bat_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { cmd->supported = 0; cmd->advertising = 0; - cmd->speed = SPEED_10; + ethtool_cmd_speed_set(cmd, SPEED_10); cmd->duplex = DUPLEX_FULL; cmd->port = PORT_TP; cmd->phy_address = 0; @@ -735,13 +923,3 @@ static u32 bat_get_link(struct net_device *dev) { return 1; } - -static u32 bat_get_rx_csum(struct net_device *dev) -{ - return 0; -} - -static int bat_set_rx_csum(struct net_device *dev, u32 data) -{ - return -EOPNOTSUPP; -} diff --git a/net/batman-adv/soft-interface.h b/net/batman-adv/soft-interface.h index 4789b6f2a0b..c24906dd1d6 100644 --- a/net/batman-adv/soft-interface.h +++ b/net/batman-adv/soft-interface.h @@ -29,8 +29,8 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface); void interface_rx(struct net_device *soft_iface, struct sk_buff *skb, struct hard_iface *recv_if, int hdr_size); -struct net_device *softif_create(char *name); +struct net_device *softif_create(const char *name); void softif_destroy(struct net_device *soft_iface); -int softif_is_valid(struct net_device *net_dev); +int softif_is_valid(const struct net_device *net_dev); #endif /* _NET_BATMAN_ADV_SOFT_INTERFACE_H_ */ diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 8d15b48d169..561f76968d5 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -22,43 +22,46 @@ #include "main.h" #include "translation-table.h" #include "soft-interface.h" +#include "hard-interface.h" #include "hash.h" #include "originator.h" -static void hna_local_purge(struct work_struct *work); -static void _hna_global_del_orig(struct bat_priv *bat_priv, - struct hna_global_entry *hna_global_entry, - char *message); +static void tt_local_purge(struct work_struct *work); +static void _tt_global_del_orig(struct bat_priv *bat_priv, + struct tt_global_entry *tt_global_entry, + const char *message); /* returns 1 if they are the same mac addr */ -static int compare_lhna(struct hlist_node *node, void *data2) +static int compare_ltt(const struct hlist_node *node, const void *data2) { - void *data1 = container_of(node, struct hna_local_entry, hash_entry); + const void *data1 = container_of(node, struct tt_local_entry, + hash_entry); return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); } /* returns 1 if they are the same mac addr */ -static int compare_ghna(struct hlist_node *node, void *data2) +static int compare_gtt(const struct hlist_node *node, const void *data2) { - void *data1 = container_of(node, struct hna_global_entry, hash_entry); + const void *data1 = container_of(node, struct tt_global_entry, + hash_entry); return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); } -static void hna_local_start_timer(struct bat_priv *bat_priv) +static void tt_local_start_timer(struct bat_priv *bat_priv) { - INIT_DELAYED_WORK(&bat_priv->hna_work, hna_local_purge); - queue_delayed_work(bat_event_workqueue, &bat_priv->hna_work, 10 * HZ); + INIT_DELAYED_WORK(&bat_priv->tt_work, tt_local_purge); + queue_delayed_work(bat_event_workqueue, &bat_priv->tt_work, 10 * HZ); } -static struct hna_local_entry *hna_local_hash_find(struct bat_priv *bat_priv, - void *data) +static struct tt_local_entry *tt_local_hash_find(struct bat_priv *bat_priv, + const void *data) { - struct hashtable_t *hash = bat_priv->hna_local_hash; + struct hashtable_t *hash = bat_priv->tt_local_hash; struct hlist_head *head; struct hlist_node *node; - struct hna_local_entry *hna_local_entry, *hna_local_entry_tmp = NULL; + struct tt_local_entry *tt_local_entry, *tt_local_entry_tmp = NULL; int index; if (!hash) @@ -68,26 +71,26 @@ static struct hna_local_entry *hna_local_hash_find(struct bat_priv *bat_priv, head = &hash->table[index]; rcu_read_lock(); - hlist_for_each_entry_rcu(hna_local_entry, node, head, hash_entry) { - if (!compare_eth(hna_local_entry, data)) + hlist_for_each_entry_rcu(tt_local_entry, node, head, hash_entry) { + if (!compare_eth(tt_local_entry, data)) continue; - hna_local_entry_tmp = hna_local_entry; + tt_local_entry_tmp = tt_local_entry; break; } rcu_read_unlock(); - return hna_local_entry_tmp; + return tt_local_entry_tmp; } -static struct hna_global_entry *hna_global_hash_find(struct bat_priv *bat_priv, - void *data) +static struct tt_global_entry *tt_global_hash_find(struct bat_priv *bat_priv, + const void *data) { - struct hashtable_t *hash = bat_priv->hna_global_hash; + struct hashtable_t *hash = bat_priv->tt_global_hash; struct hlist_head *head; struct hlist_node *node; - struct hna_global_entry *hna_global_entry; - struct hna_global_entry *hna_global_entry_tmp = NULL; + struct tt_global_entry *tt_global_entry; + struct tt_global_entry *tt_global_entry_tmp = NULL; int index; if (!hash) @@ -97,125 +100,125 @@ static struct hna_global_entry *hna_global_hash_find(struct bat_priv *bat_priv, head = &hash->table[index]; rcu_read_lock(); - hlist_for_each_entry_rcu(hna_global_entry, node, head, hash_entry) { - if (!compare_eth(hna_global_entry, data)) + hlist_for_each_entry_rcu(tt_global_entry, node, head, hash_entry) { + if (!compare_eth(tt_global_entry, data)) continue; - hna_global_entry_tmp = hna_global_entry; + tt_global_entry_tmp = tt_global_entry; break; } rcu_read_unlock(); - return hna_global_entry_tmp; + return tt_global_entry_tmp; } -int hna_local_init(struct bat_priv *bat_priv) +int tt_local_init(struct bat_priv *bat_priv) { - if (bat_priv->hna_local_hash) + if (bat_priv->tt_local_hash) return 1; - bat_priv->hna_local_hash = hash_new(1024); + bat_priv->tt_local_hash = hash_new(1024); - if (!bat_priv->hna_local_hash) + if (!bat_priv->tt_local_hash) return 0; - atomic_set(&bat_priv->hna_local_changed, 0); - hna_local_start_timer(bat_priv); + atomic_set(&bat_priv->tt_local_changed, 0); + tt_local_start_timer(bat_priv); return 1; } -void hna_local_add(struct net_device *soft_iface, uint8_t *addr) +void tt_local_add(struct net_device *soft_iface, const uint8_t *addr) { struct bat_priv *bat_priv = netdev_priv(soft_iface); - struct hna_local_entry *hna_local_entry; - struct hna_global_entry *hna_global_entry; + struct tt_local_entry *tt_local_entry; + struct tt_global_entry *tt_global_entry; int required_bytes; - spin_lock_bh(&bat_priv->hna_lhash_lock); - hna_local_entry = hna_local_hash_find(bat_priv, addr); - spin_unlock_bh(&bat_priv->hna_lhash_lock); + spin_lock_bh(&bat_priv->tt_lhash_lock); + tt_local_entry = tt_local_hash_find(bat_priv, addr); + spin_unlock_bh(&bat_priv->tt_lhash_lock); - if (hna_local_entry) { - hna_local_entry->last_seen = jiffies; + if (tt_local_entry) { + tt_local_entry->last_seen = jiffies; return; } /* only announce as many hosts as possible in the batman-packet and - space in batman_packet->num_hna That also should give a limit to + space in batman_packet->num_tt That also should give a limit to MAC-flooding. */ - required_bytes = (bat_priv->num_local_hna + 1) * ETH_ALEN; + required_bytes = (bat_priv->num_local_tt + 1) * ETH_ALEN; required_bytes += BAT_PACKET_LEN; if ((required_bytes > ETH_DATA_LEN) || (atomic_read(&bat_priv->aggregated_ogms) && required_bytes > MAX_AGGREGATION_BYTES) || - (bat_priv->num_local_hna + 1 > 255)) { + (bat_priv->num_local_tt + 1 > 255)) { bat_dbg(DBG_ROUTES, bat_priv, - "Can't add new local hna entry (%pM): " - "number of local hna entries exceeds packet size\n", + "Can't add new local tt entry (%pM): " + "number of local tt entries exceeds packet size\n", addr); return; } bat_dbg(DBG_ROUTES, bat_priv, - "Creating new local hna entry: %pM\n", addr); + "Creating new local tt entry: %pM\n", addr); - hna_local_entry = kmalloc(sizeof(struct hna_local_entry), GFP_ATOMIC); - if (!hna_local_entry) + tt_local_entry = kmalloc(sizeof(*tt_local_entry), GFP_ATOMIC); + if (!tt_local_entry) return; - memcpy(hna_local_entry->addr, addr, ETH_ALEN); - hna_local_entry->last_seen = jiffies; + memcpy(tt_local_entry->addr, addr, ETH_ALEN); + tt_local_entry->last_seen = jiffies; /* the batman interface mac address should never be purged */ if (compare_eth(addr, soft_iface->dev_addr)) - hna_local_entry->never_purge = 1; + tt_local_entry->never_purge = 1; else - hna_local_entry->never_purge = 0; + tt_local_entry->never_purge = 0; - spin_lock_bh(&bat_priv->hna_lhash_lock); + spin_lock_bh(&bat_priv->tt_lhash_lock); - hash_add(bat_priv->hna_local_hash, compare_lhna, choose_orig, - hna_local_entry, &hna_local_entry->hash_entry); - bat_priv->num_local_hna++; - atomic_set(&bat_priv->hna_local_changed, 1); + hash_add(bat_priv->tt_local_hash, compare_ltt, choose_orig, + tt_local_entry, &tt_local_entry->hash_entry); + bat_priv->num_local_tt++; + atomic_set(&bat_priv->tt_local_changed, 1); - spin_unlock_bh(&bat_priv->hna_lhash_lock); + spin_unlock_bh(&bat_priv->tt_lhash_lock); /* remove address from global hash if present */ - spin_lock_bh(&bat_priv->hna_ghash_lock); + spin_lock_bh(&bat_priv->tt_ghash_lock); - hna_global_entry = hna_global_hash_find(bat_priv, addr); + tt_global_entry = tt_global_hash_find(bat_priv, addr); - if (hna_global_entry) - _hna_global_del_orig(bat_priv, hna_global_entry, - "local hna received"); + if (tt_global_entry) + _tt_global_del_orig(bat_priv, tt_global_entry, + "local tt received"); - spin_unlock_bh(&bat_priv->hna_ghash_lock); + spin_unlock_bh(&bat_priv->tt_ghash_lock); } -int hna_local_fill_buffer(struct bat_priv *bat_priv, +int tt_local_fill_buffer(struct bat_priv *bat_priv, unsigned char *buff, int buff_len) { - struct hashtable_t *hash = bat_priv->hna_local_hash; - struct hna_local_entry *hna_local_entry; + struct hashtable_t *hash = bat_priv->tt_local_hash; + struct tt_local_entry *tt_local_entry; struct hlist_node *node; struct hlist_head *head; int i, count = 0; - spin_lock_bh(&bat_priv->hna_lhash_lock); + spin_lock_bh(&bat_priv->tt_lhash_lock); for (i = 0; i < hash->size; i++) { head = &hash->table[i]; rcu_read_lock(); - hlist_for_each_entry_rcu(hna_local_entry, node, + hlist_for_each_entry_rcu(tt_local_entry, node, head, hash_entry) { if (buff_len < (count + 1) * ETH_ALEN) break; - memcpy(buff + (count * ETH_ALEN), hna_local_entry->addr, + memcpy(buff + (count * ETH_ALEN), tt_local_entry->addr, ETH_ALEN); count++; @@ -223,37 +226,47 @@ int hna_local_fill_buffer(struct bat_priv *bat_priv, rcu_read_unlock(); } - /* if we did not get all new local hnas see you next time ;-) */ - if (count == bat_priv->num_local_hna) - atomic_set(&bat_priv->hna_local_changed, 0); + /* if we did not get all new local tts see you next time ;-) */ + if (count == bat_priv->num_local_tt) + atomic_set(&bat_priv->tt_local_changed, 0); - spin_unlock_bh(&bat_priv->hna_lhash_lock); + spin_unlock_bh(&bat_priv->tt_lhash_lock); return count; } -int hna_local_seq_print_text(struct seq_file *seq, void *offset) +int tt_local_seq_print_text(struct seq_file *seq, void *offset) { struct net_device *net_dev = (struct net_device *)seq->private; struct bat_priv *bat_priv = netdev_priv(net_dev); - struct hashtable_t *hash = bat_priv->hna_local_hash; - struct hna_local_entry *hna_local_entry; + struct hashtable_t *hash = bat_priv->tt_local_hash; + struct tt_local_entry *tt_local_entry; + struct hard_iface *primary_if; struct hlist_node *node; struct hlist_head *head; size_t buf_size, pos; char *buff; - int i; + int i, ret = 0; - if (!bat_priv->primary_if) { - return seq_printf(seq, "BATMAN mesh %s disabled - " - "please specify interfaces to enable it\n", - net_dev->name); + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) { + ret = seq_printf(seq, "BATMAN mesh %s disabled - " + "please specify interfaces to enable it\n", + net_dev->name); + goto out; + } + + if (primary_if->if_status != IF_ACTIVE) { + ret = seq_printf(seq, "BATMAN mesh %s disabled - " + "primary interface not active\n", + net_dev->name); + goto out; } seq_printf(seq, "Locally retrieved addresses (from %s) " - "announced via HNA:\n", + "announced via TT:\n", net_dev->name); - spin_lock_bh(&bat_priv->hna_lhash_lock); + spin_lock_bh(&bat_priv->tt_lhash_lock); buf_size = 1; /* Estimate length for: " * xx:xx:xx:xx:xx:xx\n" */ @@ -268,8 +281,9 @@ int hna_local_seq_print_text(struct seq_file *seq, void *offset) buff = kmalloc(buf_size, GFP_ATOMIC); if (!buff) { - spin_unlock_bh(&bat_priv->hna_lhash_lock); - return -ENOMEM; + spin_unlock_bh(&bat_priv->tt_lhash_lock); + ret = -ENOMEM; + goto out; } buff[0] = '\0'; @@ -279,211 +293,224 @@ int hna_local_seq_print_text(struct seq_file *seq, void *offset) head = &hash->table[i]; rcu_read_lock(); - hlist_for_each_entry_rcu(hna_local_entry, node, + hlist_for_each_entry_rcu(tt_local_entry, node, head, hash_entry) { pos += snprintf(buff + pos, 22, " * %pM\n", - hna_local_entry->addr); + tt_local_entry->addr); } rcu_read_unlock(); } - spin_unlock_bh(&bat_priv->hna_lhash_lock); + spin_unlock_bh(&bat_priv->tt_lhash_lock); seq_printf(seq, "%s", buff); kfree(buff); - return 0; +out: + if (primary_if) + hardif_free_ref(primary_if); + return ret; } -static void _hna_local_del(struct hlist_node *node, void *arg) +static void _tt_local_del(struct hlist_node *node, void *arg) { - struct bat_priv *bat_priv = (struct bat_priv *)arg; - void *data = container_of(node, struct hna_local_entry, hash_entry); + struct bat_priv *bat_priv = arg; + void *data = container_of(node, struct tt_local_entry, hash_entry); kfree(data); - bat_priv->num_local_hna--; - atomic_set(&bat_priv->hna_local_changed, 1); + bat_priv->num_local_tt--; + atomic_set(&bat_priv->tt_local_changed, 1); } -static void hna_local_del(struct bat_priv *bat_priv, - struct hna_local_entry *hna_local_entry, - char *message) +static void tt_local_del(struct bat_priv *bat_priv, + struct tt_local_entry *tt_local_entry, + const char *message) { - bat_dbg(DBG_ROUTES, bat_priv, "Deleting local hna entry (%pM): %s\n", - hna_local_entry->addr, message); + bat_dbg(DBG_ROUTES, bat_priv, "Deleting local tt entry (%pM): %s\n", + tt_local_entry->addr, message); - hash_remove(bat_priv->hna_local_hash, compare_lhna, choose_orig, - hna_local_entry->addr); - _hna_local_del(&hna_local_entry->hash_entry, bat_priv); + hash_remove(bat_priv->tt_local_hash, compare_ltt, choose_orig, + tt_local_entry->addr); + _tt_local_del(&tt_local_entry->hash_entry, bat_priv); } -void hna_local_remove(struct bat_priv *bat_priv, - uint8_t *addr, char *message) +void tt_local_remove(struct bat_priv *bat_priv, + const uint8_t *addr, const char *message) { - struct hna_local_entry *hna_local_entry; + struct tt_local_entry *tt_local_entry; - spin_lock_bh(&bat_priv->hna_lhash_lock); + spin_lock_bh(&bat_priv->tt_lhash_lock); - hna_local_entry = hna_local_hash_find(bat_priv, addr); + tt_local_entry = tt_local_hash_find(bat_priv, addr); - if (hna_local_entry) - hna_local_del(bat_priv, hna_local_entry, message); + if (tt_local_entry) + tt_local_del(bat_priv, tt_local_entry, message); - spin_unlock_bh(&bat_priv->hna_lhash_lock); + spin_unlock_bh(&bat_priv->tt_lhash_lock); } -static void hna_local_purge(struct work_struct *work) +static void tt_local_purge(struct work_struct *work) { struct delayed_work *delayed_work = container_of(work, struct delayed_work, work); struct bat_priv *bat_priv = - container_of(delayed_work, struct bat_priv, hna_work); - struct hashtable_t *hash = bat_priv->hna_local_hash; - struct hna_local_entry *hna_local_entry; + container_of(delayed_work, struct bat_priv, tt_work); + struct hashtable_t *hash = bat_priv->tt_local_hash; + struct tt_local_entry *tt_local_entry; struct hlist_node *node, *node_tmp; struct hlist_head *head; unsigned long timeout; int i; - spin_lock_bh(&bat_priv->hna_lhash_lock); + spin_lock_bh(&bat_priv->tt_lhash_lock); for (i = 0; i < hash->size; i++) { head = &hash->table[i]; - hlist_for_each_entry_safe(hna_local_entry, node, node_tmp, + hlist_for_each_entry_safe(tt_local_entry, node, node_tmp, head, hash_entry) { - if (hna_local_entry->never_purge) + if (tt_local_entry->never_purge) continue; - timeout = hna_local_entry->last_seen; - timeout += LOCAL_HNA_TIMEOUT * HZ; + timeout = tt_local_entry->last_seen; + timeout += TT_LOCAL_TIMEOUT * HZ; if (time_before(jiffies, timeout)) continue; - hna_local_del(bat_priv, hna_local_entry, + tt_local_del(bat_priv, tt_local_entry, "address timed out"); } } - spin_unlock_bh(&bat_priv->hna_lhash_lock); - hna_local_start_timer(bat_priv); + spin_unlock_bh(&bat_priv->tt_lhash_lock); + tt_local_start_timer(bat_priv); } -void hna_local_free(struct bat_priv *bat_priv) +void tt_local_free(struct bat_priv *bat_priv) { - if (!bat_priv->hna_local_hash) + if (!bat_priv->tt_local_hash) return; - cancel_delayed_work_sync(&bat_priv->hna_work); - hash_delete(bat_priv->hna_local_hash, _hna_local_del, bat_priv); - bat_priv->hna_local_hash = NULL; + cancel_delayed_work_sync(&bat_priv->tt_work); + hash_delete(bat_priv->tt_local_hash, _tt_local_del, bat_priv); + bat_priv->tt_local_hash = NULL; } -int hna_global_init(struct bat_priv *bat_priv) +int tt_global_init(struct bat_priv *bat_priv) { - if (bat_priv->hna_global_hash) + if (bat_priv->tt_global_hash) return 1; - bat_priv->hna_global_hash = hash_new(1024); + bat_priv->tt_global_hash = hash_new(1024); - if (!bat_priv->hna_global_hash) + if (!bat_priv->tt_global_hash) return 0; return 1; } -void hna_global_add_orig(struct bat_priv *bat_priv, +void tt_global_add_orig(struct bat_priv *bat_priv, struct orig_node *orig_node, - unsigned char *hna_buff, int hna_buff_len) + const unsigned char *tt_buff, int tt_buff_len) { - struct hna_global_entry *hna_global_entry; - struct hna_local_entry *hna_local_entry; - int hna_buff_count = 0; - unsigned char *hna_ptr; + struct tt_global_entry *tt_global_entry; + struct tt_local_entry *tt_local_entry; + int tt_buff_count = 0; + const unsigned char *tt_ptr; - while ((hna_buff_count + 1) * ETH_ALEN <= hna_buff_len) { - spin_lock_bh(&bat_priv->hna_ghash_lock); + while ((tt_buff_count + 1) * ETH_ALEN <= tt_buff_len) { + spin_lock_bh(&bat_priv->tt_ghash_lock); - hna_ptr = hna_buff + (hna_buff_count * ETH_ALEN); - hna_global_entry = hna_global_hash_find(bat_priv, hna_ptr); + tt_ptr = tt_buff + (tt_buff_count * ETH_ALEN); + tt_global_entry = tt_global_hash_find(bat_priv, tt_ptr); - if (!hna_global_entry) { - spin_unlock_bh(&bat_priv->hna_ghash_lock); + if (!tt_global_entry) { + spin_unlock_bh(&bat_priv->tt_ghash_lock); - hna_global_entry = - kmalloc(sizeof(struct hna_global_entry), - GFP_ATOMIC); + tt_global_entry = kmalloc(sizeof(*tt_global_entry), + GFP_ATOMIC); - if (!hna_global_entry) + if (!tt_global_entry) break; - memcpy(hna_global_entry->addr, hna_ptr, ETH_ALEN); + memcpy(tt_global_entry->addr, tt_ptr, ETH_ALEN); bat_dbg(DBG_ROUTES, bat_priv, - "Creating new global hna entry: " + "Creating new global tt entry: " "%pM (via %pM)\n", - hna_global_entry->addr, orig_node->orig); + tt_global_entry->addr, orig_node->orig); - spin_lock_bh(&bat_priv->hna_ghash_lock); - hash_add(bat_priv->hna_global_hash, compare_ghna, - choose_orig, hna_global_entry, - &hna_global_entry->hash_entry); + spin_lock_bh(&bat_priv->tt_ghash_lock); + hash_add(bat_priv->tt_global_hash, compare_gtt, + choose_orig, tt_global_entry, + &tt_global_entry->hash_entry); } - hna_global_entry->orig_node = orig_node; - spin_unlock_bh(&bat_priv->hna_ghash_lock); + tt_global_entry->orig_node = orig_node; + spin_unlock_bh(&bat_priv->tt_ghash_lock); /* remove address from local hash if present */ - spin_lock_bh(&bat_priv->hna_lhash_lock); + spin_lock_bh(&bat_priv->tt_lhash_lock); - hna_ptr = hna_buff + (hna_buff_count * ETH_ALEN); - hna_local_entry = hna_local_hash_find(bat_priv, hna_ptr); + tt_ptr = tt_buff + (tt_buff_count * ETH_ALEN); + tt_local_entry = tt_local_hash_find(bat_priv, tt_ptr); - if (hna_local_entry) - hna_local_del(bat_priv, hna_local_entry, - "global hna received"); + if (tt_local_entry) + tt_local_del(bat_priv, tt_local_entry, + "global tt received"); - spin_unlock_bh(&bat_priv->hna_lhash_lock); + spin_unlock_bh(&bat_priv->tt_lhash_lock); - hna_buff_count++; + tt_buff_count++; } /* initialize, and overwrite if malloc succeeds */ - orig_node->hna_buff = NULL; - orig_node->hna_buff_len = 0; - - if (hna_buff_len > 0) { - orig_node->hna_buff = kmalloc(hna_buff_len, GFP_ATOMIC); - if (orig_node->hna_buff) { - memcpy(orig_node->hna_buff, hna_buff, hna_buff_len); - orig_node->hna_buff_len = hna_buff_len; + orig_node->tt_buff = NULL; + orig_node->tt_buff_len = 0; + + if (tt_buff_len > 0) { + orig_node->tt_buff = kmalloc(tt_buff_len, GFP_ATOMIC); + if (orig_node->tt_buff) { + memcpy(orig_node->tt_buff, tt_buff, tt_buff_len); + orig_node->tt_buff_len = tt_buff_len; } } } -int hna_global_seq_print_text(struct seq_file *seq, void *offset) +int tt_global_seq_print_text(struct seq_file *seq, void *offset) { struct net_device *net_dev = (struct net_device *)seq->private; struct bat_priv *bat_priv = netdev_priv(net_dev); - struct hashtable_t *hash = bat_priv->hna_global_hash; - struct hna_global_entry *hna_global_entry; + struct hashtable_t *hash = bat_priv->tt_global_hash; + struct tt_global_entry *tt_global_entry; + struct hard_iface *primary_if; struct hlist_node *node; struct hlist_head *head; size_t buf_size, pos; char *buff; - int i; + int i, ret = 0; - if (!bat_priv->primary_if) { - return seq_printf(seq, "BATMAN mesh %s disabled - " - "please specify interfaces to enable it\n", - net_dev->name); + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) { + ret = seq_printf(seq, "BATMAN mesh %s disabled - please " + "specify interfaces to enable it\n", + net_dev->name); + goto out; } - seq_printf(seq, "Globally announced HNAs received via the mesh %s\n", + if (primary_if->if_status != IF_ACTIVE) { + ret = seq_printf(seq, "BATMAN mesh %s disabled - " + "primary interface not active\n", + net_dev->name); + goto out; + } + + seq_printf(seq, + "Globally announced TT entries received via the mesh %s\n", net_dev->name); - spin_lock_bh(&bat_priv->hna_ghash_lock); + spin_lock_bh(&bat_priv->tt_ghash_lock); buf_size = 1; /* Estimate length for: " * xx:xx:xx:xx:xx:xx via xx:xx:xx:xx:xx:xx\n"*/ @@ -498,8 +525,9 @@ int hna_global_seq_print_text(struct seq_file *seq, void *offset) buff = kmalloc(buf_size, GFP_ATOMIC); if (!buff) { - spin_unlock_bh(&bat_priv->hna_ghash_lock); - return -ENOMEM; + spin_unlock_bh(&bat_priv->tt_ghash_lock); + ret = -ENOMEM; + goto out; } buff[0] = '\0'; pos = 0; @@ -508,101 +536,105 @@ int hna_global_seq_print_text(struct seq_file *seq, void *offset) head = &hash->table[i]; rcu_read_lock(); - hlist_for_each_entry_rcu(hna_global_entry, node, + hlist_for_each_entry_rcu(tt_global_entry, node, head, hash_entry) { pos += snprintf(buff + pos, 44, " * %pM via %pM\n", - hna_global_entry->addr, - hna_global_entry->orig_node->orig); + tt_global_entry->addr, + tt_global_entry->orig_node->orig); } rcu_read_unlock(); } - spin_unlock_bh(&bat_priv->hna_ghash_lock); + spin_unlock_bh(&bat_priv->tt_ghash_lock); seq_printf(seq, "%s", buff); kfree(buff); - return 0; +out: + if (primary_if) + hardif_free_ref(primary_if); + return ret; } -static void _hna_global_del_orig(struct bat_priv *bat_priv, - struct hna_global_entry *hna_global_entry, - char *message) +static void _tt_global_del_orig(struct bat_priv *bat_priv, + struct tt_global_entry *tt_global_entry, + const char *message) { bat_dbg(DBG_ROUTES, bat_priv, - "Deleting global hna entry %pM (via %pM): %s\n", - hna_global_entry->addr, hna_global_entry->orig_node->orig, + "Deleting global tt entry %pM (via %pM): %s\n", + tt_global_entry->addr, tt_global_entry->orig_node->orig, message); - hash_remove(bat_priv->hna_global_hash, compare_ghna, choose_orig, - hna_global_entry->addr); - kfree(hna_global_entry); + hash_remove(bat_priv->tt_global_hash, compare_gtt, choose_orig, + tt_global_entry->addr); + kfree(tt_global_entry); } -void hna_global_del_orig(struct bat_priv *bat_priv, - struct orig_node *orig_node, char *message) +void tt_global_del_orig(struct bat_priv *bat_priv, + struct orig_node *orig_node, const char *message) { - struct hna_global_entry *hna_global_entry; - int hna_buff_count = 0; - unsigned char *hna_ptr; + struct tt_global_entry *tt_global_entry; + int tt_buff_count = 0; + unsigned char *tt_ptr; - if (orig_node->hna_buff_len == 0) + if (orig_node->tt_buff_len == 0) return; - spin_lock_bh(&bat_priv->hna_ghash_lock); + spin_lock_bh(&bat_priv->tt_ghash_lock); - while ((hna_buff_count + 1) * ETH_ALEN <= orig_node->hna_buff_len) { - hna_ptr = orig_node->hna_buff + (hna_buff_count * ETH_ALEN); - hna_global_entry = hna_global_hash_find(bat_priv, hna_ptr); + while ((tt_buff_count + 1) * ETH_ALEN <= orig_node->tt_buff_len) { + tt_ptr = orig_node->tt_buff + (tt_buff_count * ETH_ALEN); + tt_global_entry = tt_global_hash_find(bat_priv, tt_ptr); - if ((hna_global_entry) && - (hna_global_entry->orig_node == orig_node)) - _hna_global_del_orig(bat_priv, hna_global_entry, + if ((tt_global_entry) && + (tt_global_entry->orig_node == orig_node)) + _tt_global_del_orig(bat_priv, tt_global_entry, message); - hna_buff_count++; + tt_buff_count++; } - spin_unlock_bh(&bat_priv->hna_ghash_lock); + spin_unlock_bh(&bat_priv->tt_ghash_lock); - orig_node->hna_buff_len = 0; - kfree(orig_node->hna_buff); - orig_node->hna_buff = NULL; + orig_node->tt_buff_len = 0; + kfree(orig_node->tt_buff); + orig_node->tt_buff = NULL; } -static void hna_global_del(struct hlist_node *node, void *arg) +static void tt_global_del(struct hlist_node *node, void *arg) { - void *data = container_of(node, struct hna_global_entry, hash_entry); + void *data = container_of(node, struct tt_global_entry, hash_entry); kfree(data); } -void hna_global_free(struct bat_priv *bat_priv) +void tt_global_free(struct bat_priv *bat_priv) { - if (!bat_priv->hna_global_hash) + if (!bat_priv->tt_global_hash) return; - hash_delete(bat_priv->hna_global_hash, hna_global_del, NULL); - bat_priv->hna_global_hash = NULL; + hash_delete(bat_priv->tt_global_hash, tt_global_del, NULL); + bat_priv->tt_global_hash = NULL; } -struct orig_node *transtable_search(struct bat_priv *bat_priv, uint8_t *addr) +struct orig_node *transtable_search(struct bat_priv *bat_priv, + const uint8_t *addr) { - struct hna_global_entry *hna_global_entry; + struct tt_global_entry *tt_global_entry; struct orig_node *orig_node = NULL; - spin_lock_bh(&bat_priv->hna_ghash_lock); - hna_global_entry = hna_global_hash_find(bat_priv, addr); + spin_lock_bh(&bat_priv->tt_ghash_lock); + tt_global_entry = tt_global_hash_find(bat_priv, addr); - if (!hna_global_entry) + if (!tt_global_entry) goto out; - if (!atomic_inc_not_zero(&hna_global_entry->orig_node->refcount)) + if (!atomic_inc_not_zero(&tt_global_entry->orig_node->refcount)) goto out; - orig_node = hna_global_entry->orig_node; + orig_node = tt_global_entry->orig_node; out: - spin_unlock_bh(&bat_priv->hna_ghash_lock); + spin_unlock_bh(&bat_priv->tt_ghash_lock); return orig_node; } diff --git a/net/batman-adv/translation-table.h b/net/batman-adv/translation-table.h index f19931ca145..0f2b9905cfc 100644 --- a/net/batman-adv/translation-table.h +++ b/net/batman-adv/translation-table.h @@ -22,22 +22,23 @@ #ifndef _NET_BATMAN_ADV_TRANSLATION_TABLE_H_ #define _NET_BATMAN_ADV_TRANSLATION_TABLE_H_ -int hna_local_init(struct bat_priv *bat_priv); -void hna_local_add(struct net_device *soft_iface, uint8_t *addr); -void hna_local_remove(struct bat_priv *bat_priv, - uint8_t *addr, char *message); -int hna_local_fill_buffer(struct bat_priv *bat_priv, +int tt_local_init(struct bat_priv *bat_priv); +void tt_local_add(struct net_device *soft_iface, const uint8_t *addr); +void tt_local_remove(struct bat_priv *bat_priv, + const uint8_t *addr, const char *message); +int tt_local_fill_buffer(struct bat_priv *bat_priv, unsigned char *buff, int buff_len); -int hna_local_seq_print_text(struct seq_file *seq, void *offset); -void hna_local_free(struct bat_priv *bat_priv); -int hna_global_init(struct bat_priv *bat_priv); -void hna_global_add_orig(struct bat_priv *bat_priv, - struct orig_node *orig_node, - unsigned char *hna_buff, int hna_buff_len); -int hna_global_seq_print_text(struct seq_file *seq, void *offset); -void hna_global_del_orig(struct bat_priv *bat_priv, - struct orig_node *orig_node, char *message); -void hna_global_free(struct bat_priv *bat_priv); -struct orig_node *transtable_search(struct bat_priv *bat_priv, uint8_t *addr); +int tt_local_seq_print_text(struct seq_file *seq, void *offset); +void tt_local_free(struct bat_priv *bat_priv); +int tt_global_init(struct bat_priv *bat_priv); +void tt_global_add_orig(struct bat_priv *bat_priv, + struct orig_node *orig_node, + const unsigned char *tt_buff, int tt_buff_len); +int tt_global_seq_print_text(struct seq_file *seq, void *offset); +void tt_global_del_orig(struct bat_priv *bat_priv, + struct orig_node *orig_node, const char *message); +void tt_global_free(struct bat_priv *bat_priv); +struct orig_node *transtable_search(struct bat_priv *bat_priv, + const uint8_t *addr); #endif /* _NET_BATMAN_ADV_TRANSLATION_TABLE_H_ */ diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 75123b1ae0d..65b32223d10 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -75,8 +75,8 @@ struct orig_node { unsigned long batman_seqno_reset; uint8_t gw_flags; uint8_t flags; - unsigned char *hna_buff; - int16_t hna_buff_len; + unsigned char *tt_buff; + int16_t tt_buff_len; uint32_t last_real_seqno; uint8_t last_ttl; unsigned long bcast_bits[NUM_WORDS]; @@ -89,11 +89,11 @@ struct orig_node { struct hlist_node hash_entry; struct bat_priv *bat_priv; unsigned long last_frag_packet; - spinlock_t ogm_cnt_lock; /* protects: bcast_own, bcast_own_sum, - * neigh_node->real_bits, - * neigh_node->real_packet_count */ - spinlock_t bcast_seqno_lock; /* protects bcast_bits, - * last_bcast_seqno */ + /* ogm_cnt_lock protects: bcast_own, bcast_own_sum, + * neigh_node->real_bits, neigh_node->real_packet_count */ + spinlock_t ogm_cnt_lock; + /* bcast_seqno_lock protects bcast_bits, last_bcast_seqno */ + spinlock_t bcast_seqno_lock; atomic_t bond_candidates; struct list_head bond_list; }; @@ -146,34 +146,34 @@ struct bat_priv { atomic_t bcast_queue_left; atomic_t batman_queue_left; char num_ifaces; - struct hlist_head softif_neigh_list; - struct softif_neigh __rcu *softif_neigh; struct debug_log *debug_log; - struct hard_iface *primary_if; struct kobject *mesh_obj; struct dentry *debug_dir; struct hlist_head forw_bat_list; struct hlist_head forw_bcast_list; struct hlist_head gw_list; + struct hlist_head softif_neigh_vids; struct list_head vis_send_list; struct hashtable_t *orig_hash; - struct hashtable_t *hna_local_hash; - struct hashtable_t *hna_global_hash; + struct hashtable_t *tt_local_hash; + struct hashtable_t *tt_global_hash; struct hashtable_t *vis_hash; spinlock_t forw_bat_list_lock; /* protects forw_bat_list */ spinlock_t forw_bcast_list_lock; /* protects */ - spinlock_t hna_lhash_lock; /* protects hna_local_hash */ - spinlock_t hna_ghash_lock; /* protects hna_global_hash */ + spinlock_t tt_lhash_lock; /* protects tt_local_hash */ + spinlock_t tt_ghash_lock; /* protects tt_global_hash */ spinlock_t gw_list_lock; /* protects gw_list and curr_gw */ spinlock_t vis_hash_lock; /* protects vis_hash */ spinlock_t vis_list_lock; /* protects vis_info::recv_list */ spinlock_t softif_neigh_lock; /* protects soft-interface neigh list */ - int16_t num_local_hna; - atomic_t hna_local_changed; - struct delayed_work hna_work; + spinlock_t softif_neigh_vid_lock; /* protects soft-interface vid list */ + int16_t num_local_tt; + atomic_t tt_local_changed; + struct delayed_work tt_work; struct delayed_work orig_work; struct delayed_work vis_work; struct gw_node __rcu *curr_gw; /* rcu protected pointer */ + struct hard_iface __rcu *primary_if; /* rcu protected pointer */ struct vis_info *my_vis_info; }; @@ -192,14 +192,14 @@ struct socket_packet { struct icmp_packet_rr icmp_packet; }; -struct hna_local_entry { +struct tt_local_entry { uint8_t addr[ETH_ALEN]; unsigned long last_seen; char never_purge; struct hlist_node hash_entry; }; -struct hna_global_entry { +struct tt_global_entry { uint8_t addr[ETH_ALEN]; struct orig_node *orig_node; struct hlist_node hash_entry; @@ -246,10 +246,10 @@ struct frag_packet_list_entry { }; struct vis_info { - unsigned long first_seen; - struct list_head recv_list; - /* list of server-neighbors we received a vis-packet - * from. we should not reply to them. */ + unsigned long first_seen; + /* list of server-neighbors we received a vis-packet + * from. we should not reply to them. */ + struct list_head recv_list; struct list_head send_list; struct kref refcount; struct hlist_node hash_entry; @@ -262,7 +262,7 @@ struct vis_info { struct vis_info_entry { uint8_t src[ETH_ALEN]; uint8_t dest[ETH_ALEN]; - uint8_t quality; /* quality = 0 means HNA */ + uint8_t quality; /* quality = 0 client */ } __packed; struct recvlist_node { @@ -270,11 +270,20 @@ struct recvlist_node { uint8_t mac[ETH_ALEN]; }; +struct softif_neigh_vid { + struct hlist_node list; + struct bat_priv *bat_priv; + short vid; + atomic_t refcount; + struct softif_neigh __rcu *softif_neigh; + struct rcu_head rcu; + struct hlist_head softif_neigh_list; +}; + struct softif_neigh { struct hlist_node list; uint8_t addr[ETH_ALEN]; unsigned long last_seen; - short vid; atomic_t refcount; struct rcu_head rcu; }; diff --git a/net/batman-adv/unicast.c b/net/batman-adv/unicast.c index d46acc81513..6eabf42f882 100644 --- a/net/batman-adv/unicast.c +++ b/net/batman-adv/unicast.c @@ -39,8 +39,8 @@ static struct sk_buff *frag_merge_packet(struct list_head *head, (struct unicast_frag_packet *)skb->data; struct sk_buff *tmp_skb; struct unicast_packet *unicast_packet; - int hdr_len = sizeof(struct unicast_packet); - int uni_diff = sizeof(struct unicast_frag_packet) - hdr_len; + int hdr_len = sizeof(*unicast_packet); + int uni_diff = sizeof(*up) - hdr_len; /* set skb to the first part and tmp_skb to the second part */ if (up->flags & UNI_FRAG_HEAD) { @@ -53,7 +53,7 @@ static struct sk_buff *frag_merge_packet(struct list_head *head, if (skb_linearize(skb) < 0 || skb_linearize(tmp_skb) < 0) goto err; - skb_pull(tmp_skb, sizeof(struct unicast_frag_packet)); + skb_pull(tmp_skb, sizeof(*up)); if (pskb_expand_head(skb, 0, tmp_skb->len, GFP_ATOMIC) < 0) goto err; @@ -99,8 +99,7 @@ static int frag_create_buffer(struct list_head *head) struct frag_packet_list_entry *tfp; for (i = 0; i < FRAG_BUFFER_SIZE; i++) { - tfp = kmalloc(sizeof(struct frag_packet_list_entry), - GFP_ATOMIC); + tfp = kmalloc(sizeof(*tfp), GFP_ATOMIC); if (!tfp) { frag_list_free(head); return -ENOMEM; @@ -115,7 +114,7 @@ static int frag_create_buffer(struct list_head *head) } static struct frag_packet_list_entry *frag_search_packet(struct list_head *head, - struct unicast_frag_packet *up) + const struct unicast_frag_packet *up) { struct frag_packet_list_entry *tfp; struct unicast_frag_packet *tmp_up = NULL; @@ -218,18 +217,20 @@ out: } int frag_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv, - struct hard_iface *hard_iface, uint8_t dstaddr[]) + struct hard_iface *hard_iface, const uint8_t dstaddr[]) { struct unicast_packet tmp_uc, *unicast_packet; + struct hard_iface *primary_if; struct sk_buff *frag_skb; struct unicast_frag_packet *frag1, *frag2; - int uc_hdr_len = sizeof(struct unicast_packet); - int ucf_hdr_len = sizeof(struct unicast_frag_packet); + int uc_hdr_len = sizeof(*unicast_packet); + int ucf_hdr_len = sizeof(*frag1); int data_len = skb->len - uc_hdr_len; - int large_tail = 0; + int large_tail = 0, ret = NET_RX_DROP; uint16_t seqno; - if (!bat_priv->primary_if) + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) goto dropped; frag_skb = dev_alloc_skb(data_len - (data_len / 2) + ucf_hdr_len); @@ -248,14 +249,14 @@ int frag_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv, frag1 = (struct unicast_frag_packet *)skb->data; frag2 = (struct unicast_frag_packet *)frag_skb->data; - memcpy(frag1, &tmp_uc, sizeof(struct unicast_packet)); + memcpy(frag1, &tmp_uc, sizeof(tmp_uc)); frag1->ttl--; frag1->version = COMPAT_VERSION; frag1->packet_type = BAT_UNICAST_FRAG; - memcpy(frag1->orig, bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); - memcpy(frag2, frag1, sizeof(struct unicast_frag_packet)); + memcpy(frag1->orig, primary_if->net_dev->dev_addr, ETH_ALEN); + memcpy(frag2, frag1, sizeof(*frag2)); if (data_len & 1) large_tail = UNI_FRAG_LARGETAIL; @@ -269,13 +270,17 @@ int frag_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv, send_skb_packet(skb, hard_iface, dstaddr); send_skb_packet(frag_skb, hard_iface, dstaddr); - return NET_RX_SUCCESS; + ret = NET_RX_SUCCESS; + goto out; drop_frag: kfree_skb(frag_skb); dropped: kfree_skb(skb); - return NET_RX_DROP; +out: + if (primary_if) + hardif_free_ref(primary_if); + return ret; } int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv) @@ -289,12 +294,12 @@ int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv) /* get routing information */ if (is_multicast_ether_addr(ethhdr->h_dest)) { - orig_node = (struct orig_node *)gw_get_selected_orig(bat_priv); + orig_node = gw_get_selected_orig(bat_priv); if (orig_node) goto find_router; } - /* check for hna host - increases orig_node refcount */ + /* check for tt host - increases orig_node refcount */ orig_node = transtable_search(bat_priv, ethhdr->h_dest); find_router: @@ -308,10 +313,7 @@ find_router: if (!neigh_node) goto out; - if (neigh_node->if_incoming->if_status != IF_ACTIVE) - goto out; - - if (my_skb_head_push(skb, sizeof(struct unicast_packet)) < 0) + if (my_skb_head_push(skb, sizeof(*unicast_packet)) < 0) goto out; unicast_packet = (struct unicast_packet *)skb->data; @@ -325,7 +327,7 @@ find_router: memcpy(unicast_packet->dest, orig_node->orig, ETH_ALEN); if (atomic_read(&bat_priv->fragmentation) && - data_len + sizeof(struct unicast_packet) > + data_len + sizeof(*unicast_packet) > neigh_node->if_incoming->net_dev->mtu) { /* send frag skb decreases ttl */ unicast_packet->ttl++; diff --git a/net/batman-adv/unicast.h b/net/batman-adv/unicast.h index 16ad7a9242b..62f54b95462 100644 --- a/net/batman-adv/unicast.h +++ b/net/batman-adv/unicast.h @@ -32,11 +32,11 @@ int frag_reassemble_skb(struct sk_buff *skb, struct bat_priv *bat_priv, void frag_list_free(struct list_head *head); int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv); int frag_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv, - struct hard_iface *hard_iface, uint8_t dstaddr[]); + struct hard_iface *hard_iface, const uint8_t dstaddr[]); -static inline int frag_can_reassemble(struct sk_buff *skb, int mtu) +static inline int frag_can_reassemble(const struct sk_buff *skb, int mtu) { - struct unicast_frag_packet *unicast_packet; + const struct unicast_frag_packet *unicast_packet; int uneven_correction = 0; unsigned int merged_size; @@ -49,7 +49,7 @@ static inline int frag_can_reassemble(struct sk_buff *skb, int mtu) uneven_correction = -1; } - merged_size = (skb->len - sizeof(struct unicast_frag_packet)) * 2; + merged_size = (skb->len - sizeof(*unicast_packet)) * 2; merged_size += sizeof(struct unicast_packet) + uneven_correction; return merged_size <= mtu; diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c index d4cc4f5399f..355c6e590b0 100644 --- a/net/batman-adv/vis.c +++ b/net/batman-adv/vis.c @@ -30,22 +30,6 @@ #define MAX_VIS_PACKET_SIZE 1000 -/* Returns the smallest signed integer in two's complement with the sizeof x */ -#define smallest_signed_int(x) (1u << (7u + 8u * (sizeof(x) - 1u))) - -/* Checks if a sequence number x is a predecessor/successor of y. - * they handle overflows/underflows and can correctly check for a - * predecessor/successor unless the variable sequence number has grown by - * more then 2**(bitwidth(x)-1)-1. - * This means that for a uint8_t with the maximum value 255, it would think: - * - when adding nothing - it is neither a predecessor nor a successor - * - before adding more than 127 to the starting value - it is a predecessor, - * - when adding 128 - it is neither a predecessor nor a successor, - * - after adding more than 127 to the starting value - it is a successor */ -#define seq_before(x, y) ({typeof(x) _dummy = (x - y); \ - _dummy > smallest_signed_int(_dummy); }) -#define seq_after(x, y) seq_before(y, x) - static void start_vis_timer(struct bat_priv *bat_priv); /* free the info */ @@ -68,10 +52,10 @@ static void free_info(struct kref *ref) } /* Compare two vis packets, used by the hashing algorithm */ -static int vis_info_cmp(struct hlist_node *node, void *data2) +static int vis_info_cmp(const struct hlist_node *node, const void *data2) { - struct vis_info *d1, *d2; - struct vis_packet *p1, *p2; + const struct vis_info *d1, *d2; + const struct vis_packet *p1, *p2; d1 = container_of(node, struct vis_info, hash_entry); d2 = data2; @@ -82,11 +66,11 @@ static int vis_info_cmp(struct hlist_node *node, void *data2) /* hash function to choose an entry in a hash table of given size */ /* hash algorithm from http://en.wikipedia.org/wiki/Hash_table */ -static int vis_info_choose(void *data, int size) +static int vis_info_choose(const void *data, int size) { - struct vis_info *vis_info = data; - struct vis_packet *packet; - unsigned char *key; + const struct vis_info *vis_info = data; + const struct vis_packet *packet; + const unsigned char *key; uint32_t hash = 0; size_t i; @@ -106,7 +90,7 @@ static int vis_info_choose(void *data, int size) } static struct vis_info *vis_hash_find(struct bat_priv *bat_priv, - void *data) + const void *data) { struct hashtable_t *hash = bat_priv->vis_hash; struct hlist_head *head; @@ -143,7 +127,7 @@ static void vis_data_insert_interface(const uint8_t *interface, struct hlist_node *pos; hlist_for_each_entry(entry, pos, if_list, list) { - if (compare_eth(entry->addr, (void *)interface)) + if (compare_eth(entry->addr, interface)) return; } @@ -156,7 +140,8 @@ static void vis_data_insert_interface(const uint8_t *interface, hlist_add_head(&entry->list, if_list); } -static ssize_t vis_data_read_prim_sec(char *buff, struct hlist_head *if_list) +static ssize_t vis_data_read_prim_sec(char *buff, + const struct hlist_head *if_list) { struct if_list_entry *entry; struct hlist_node *pos; @@ -189,12 +174,13 @@ static size_t vis_data_count_prim_sec(struct hlist_head *if_list) } /* read an entry */ -static ssize_t vis_data_read_entry(char *buff, struct vis_info_entry *entry, - uint8_t *src, bool primary) +static ssize_t vis_data_read_entry(char *buff, + const struct vis_info_entry *entry, + const uint8_t *src, bool primary) { /* maximal length: max(4+17+2, 3+17+1+3+2) == 26 */ if (primary && entry->quality == 0) - return sprintf(buff, "HNA %pM, ", entry->dest); + return sprintf(buff, "TT %pM, ", entry->dest); else if (compare_eth(entry->src, src)) return sprintf(buff, "TQ %pM %d, ", entry->dest, entry->quality); @@ -204,6 +190,7 @@ static ssize_t vis_data_read_entry(char *buff, struct vis_info_entry *entry, int vis_seq_print_text(struct seq_file *seq, void *offset) { + struct hard_iface *primary_if; struct hlist_node *node; struct hlist_head *head; struct vis_info *info; @@ -215,15 +202,18 @@ int vis_seq_print_text(struct seq_file *seq, void *offset) HLIST_HEAD(vis_if_list); struct if_list_entry *entry; struct hlist_node *pos, *n; - int i, j; + int i, j, ret = 0; int vis_server = atomic_read(&bat_priv->vis_mode); size_t buff_pos, buf_size; char *buff; int compare; - if ((!bat_priv->primary_if) || - (vis_server == VIS_TYPE_CLIENT_UPDATE)) - return 0; + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; + + if (vis_server == VIS_TYPE_CLIENT_UPDATE) + goto out; buf_size = 1; /* Estimate length */ @@ -235,7 +225,7 @@ int vis_seq_print_text(struct seq_file *seq, void *offset) hlist_for_each_entry_rcu(info, node, head, hash_entry) { packet = (struct vis_packet *)info->skb_packet->data; entries = (struct vis_info_entry *) - ((char *)packet + sizeof(struct vis_packet)); + ((char *)packet + sizeof(*packet)); for (j = 0; j < packet->entries; j++) { if (entries[j].quality == 0) @@ -270,7 +260,8 @@ int vis_seq_print_text(struct seq_file *seq, void *offset) buff = kmalloc(buf_size, GFP_ATOMIC); if (!buff) { spin_unlock_bh(&bat_priv->vis_hash_lock); - return -ENOMEM; + ret = -ENOMEM; + goto out; } buff[0] = '\0'; buff_pos = 0; @@ -282,7 +273,7 @@ int vis_seq_print_text(struct seq_file *seq, void *offset) hlist_for_each_entry_rcu(info, node, head, hash_entry) { packet = (struct vis_packet *)info->skb_packet->data; entries = (struct vis_info_entry *) - ((char *)packet + sizeof(struct vis_packet)); + ((char *)packet + sizeof(*packet)); for (j = 0; j < packet->entries; j++) { if (entries[j].quality == 0) @@ -328,7 +319,10 @@ int vis_seq_print_text(struct seq_file *seq, void *offset) seq_printf(seq, "%s", buff); kfree(buff); - return 0; +out: + if (primary_if) + hardif_free_ref(primary_if); + return ret; } /* add the info packet to the send list, if it was not @@ -353,11 +347,11 @@ static void send_list_del(struct vis_info *info) /* tries to add one entry to the receive list. */ static void recv_list_add(struct bat_priv *bat_priv, - struct list_head *recv_list, char *mac) + struct list_head *recv_list, const char *mac) { struct recvlist_node *entry; - entry = kmalloc(sizeof(struct recvlist_node), GFP_ATOMIC); + entry = kmalloc(sizeof(*entry), GFP_ATOMIC); if (!entry) return; @@ -369,9 +363,9 @@ static void recv_list_add(struct bat_priv *bat_priv, /* returns 1 if this mac is in the recv_list */ static int recv_list_is_in(struct bat_priv *bat_priv, - struct list_head *recv_list, char *mac) + const struct list_head *recv_list, const char *mac) { - struct recvlist_node *entry; + const struct recvlist_node *entry; spin_lock_bh(&bat_priv->vis_list_lock); list_for_each_entry(entry, recv_list, list) { @@ -404,11 +398,11 @@ static struct vis_info *add_packet(struct bat_priv *bat_priv, return NULL; /* see if the packet is already in vis_hash */ - search_elem.skb_packet = dev_alloc_skb(sizeof(struct vis_packet)); + search_elem.skb_packet = dev_alloc_skb(sizeof(*search_packet)); if (!search_elem.skb_packet) return NULL; search_packet = (struct vis_packet *)skb_put(search_elem.skb_packet, - sizeof(struct vis_packet)); + sizeof(*search_packet)); memcpy(search_packet->vis_orig, vis_packet->vis_orig, ETH_ALEN); old_info = vis_hash_find(bat_priv, &search_elem); @@ -434,27 +428,26 @@ static struct vis_info *add_packet(struct bat_priv *bat_priv, kref_put(&old_info->refcount, free_info); } - info = kmalloc(sizeof(struct vis_info), GFP_ATOMIC); + info = kmalloc(sizeof(*info), GFP_ATOMIC); if (!info) return NULL; - info->skb_packet = dev_alloc_skb(sizeof(struct vis_packet) + - vis_info_len + sizeof(struct ethhdr)); + info->skb_packet = dev_alloc_skb(sizeof(*packet) + vis_info_len + + sizeof(struct ethhdr)); if (!info->skb_packet) { kfree(info); return NULL; } skb_reserve(info->skb_packet, sizeof(struct ethhdr)); - packet = (struct vis_packet *)skb_put(info->skb_packet, - sizeof(struct vis_packet) + - vis_info_len); + packet = (struct vis_packet *)skb_put(info->skb_packet, sizeof(*packet) + + vis_info_len); kref_init(&info->refcount); INIT_LIST_HEAD(&info->send_list); INIT_LIST_HEAD(&info->recv_list); info->first_seen = jiffies; info->bat_priv = bat_priv; - memcpy(packet, vis_packet, sizeof(struct vis_packet) + vis_info_len); + memcpy(packet, vis_packet, sizeof(*packet) + vis_info_len); /* initialize and add new packet. */ *is_new = 1; @@ -591,9 +584,9 @@ static int find_best_vis_server(struct bat_priv *bat_priv, } /* Return true if the vis packet is full. */ -static bool vis_packet_full(struct vis_info *info) +static bool vis_packet_full(const struct vis_info *info) { - struct vis_packet *packet; + const struct vis_packet *packet; packet = (struct vis_packet *)info->skb_packet->data; if (MAX_VIS_PACKET_SIZE / sizeof(struct vis_info_entry) @@ -611,10 +604,10 @@ static int generate_vis_packet(struct bat_priv *bat_priv) struct hlist_head *head; struct orig_node *orig_node; struct neigh_node *router; - struct vis_info *info = (struct vis_info *)bat_priv->my_vis_info; + struct vis_info *info = bat_priv->my_vis_info; struct vis_packet *packet = (struct vis_packet *)info->skb_packet->data; struct vis_info_entry *entry; - struct hna_local_entry *hna_local_entry; + struct tt_local_entry *tt_local_entry; int best_tq = -1, i; info->first_seen = jiffies; @@ -624,7 +617,7 @@ static int generate_vis_packet(struct bat_priv *bat_priv) packet->ttl = TTL; packet->seqno = htonl(ntohl(packet->seqno) + 1); packet->entries = 0; - skb_trim(info->skb_packet, sizeof(struct vis_packet)); + skb_trim(info->skb_packet, sizeof(*packet)); if (packet->vis_type == VIS_TYPE_CLIENT_UPDATE) { best_tq = find_best_vis_server(bat_priv, info); @@ -670,29 +663,29 @@ next: rcu_read_unlock(); } - hash = bat_priv->hna_local_hash; + hash = bat_priv->tt_local_hash; - spin_lock_bh(&bat_priv->hna_lhash_lock); + spin_lock_bh(&bat_priv->tt_lhash_lock); for (i = 0; i < hash->size; i++) { head = &hash->table[i]; - hlist_for_each_entry(hna_local_entry, node, head, hash_entry) { + hlist_for_each_entry(tt_local_entry, node, head, hash_entry) { entry = (struct vis_info_entry *) skb_put(info->skb_packet, sizeof(*entry)); memset(entry->src, 0, ETH_ALEN); - memcpy(entry->dest, hna_local_entry->addr, ETH_ALEN); - entry->quality = 0; /* 0 means HNA */ + memcpy(entry->dest, tt_local_entry->addr, ETH_ALEN); + entry->quality = 0; /* 0 means TT */ packet->entries++; if (vis_packet_full(info)) { - spin_unlock_bh(&bat_priv->hna_lhash_lock); + spin_unlock_bh(&bat_priv->tt_lhash_lock); return 0; } } } - spin_unlock_bh(&bat_priv->hna_lhash_lock); + spin_unlock_bh(&bat_priv->tt_lhash_lock); return 0; unlock: @@ -815,16 +808,20 @@ out: /* only send one vis packet. called from send_vis_packets() */ static void send_vis_packet(struct bat_priv *bat_priv, struct vis_info *info) { + struct hard_iface *primary_if; struct vis_packet *packet; + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; + packet = (struct vis_packet *)info->skb_packet->data; if (packet->ttl < 2) { pr_debug("Error - can't send vis packet: ttl exceeded\n"); - return; + goto out; } - memcpy(packet->sender_orig, bat_priv->primary_if->net_dev->dev_addr, - ETH_ALEN); + memcpy(packet->sender_orig, primary_if->net_dev->dev_addr, ETH_ALEN); packet->ttl--; if (is_broadcast_ether_addr(packet->target_orig)) @@ -832,6 +829,10 @@ static void send_vis_packet(struct bat_priv *bat_priv, struct vis_info *info) else unicast_vis_packet(bat_priv, info); packet->ttl++; /* restore TTL */ + +out: + if (primary_if) + hardif_free_ref(primary_if); } /* called from timer; send (and maybe generate) vis packet. */ @@ -858,8 +859,7 @@ static void send_vis_packets(struct work_struct *work) kref_get(&info->refcount); spin_unlock_bh(&bat_priv->vis_hash_lock); - if (bat_priv->primary_if) - send_vis_packet(bat_priv, info); + send_vis_packet(bat_priv, info); spin_lock_bh(&bat_priv->vis_hash_lock); send_list_del(info); @@ -893,17 +893,15 @@ int vis_init(struct bat_priv *bat_priv) goto err; } - bat_priv->my_vis_info->skb_packet = dev_alloc_skb( - sizeof(struct vis_packet) + - MAX_VIS_PACKET_SIZE + - sizeof(struct ethhdr)); + bat_priv->my_vis_info->skb_packet = dev_alloc_skb(sizeof(*packet) + + MAX_VIS_PACKET_SIZE + + sizeof(struct ethhdr)); if (!bat_priv->my_vis_info->skb_packet) goto free_info; skb_reserve(bat_priv->my_vis_info->skb_packet, sizeof(struct ethhdr)); - packet = (struct vis_packet *)skb_put( - bat_priv->my_vis_info->skb_packet, - sizeof(struct vis_packet)); + packet = (struct vis_packet *)skb_put(bat_priv->my_vis_info->skb_packet, + sizeof(*packet)); /* prefill the vis info */ bat_priv->my_vis_info->first_seen = jiffies - diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c index cce99b0919f..c5b11af908b 100644 --- a/net/bluetooth/cmtp/core.c +++ b/net/bluetooth/cmtp/core.c @@ -346,7 +346,8 @@ int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock) bacpy(&session->bdaddr, &bt_sk(sock->sk)->dst); - session->mtu = min_t(uint, l2cap_pi(sock->sk)->omtu, l2cap_pi(sock->sk)->imtu); + session->mtu = min_t(uint, l2cap_pi(sock->sk)->chan->omtu, + l2cap_pi(sock->sk)->chan->imtu); BT_DBG("mtu %d", session->mtu); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 7a6f56b2f49..3163330cd4f 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -269,6 +269,19 @@ static void hci_conn_idle(unsigned long arg) hci_conn_enter_sniff_mode(conn); } +static void hci_conn_auto_accept(unsigned long arg) +{ + struct hci_conn *conn = (void *) arg; + struct hci_dev *hdev = conn->hdev; + + hci_dev_lock(hdev); + + hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_REPLY, sizeof(conn->dst), + &conn->dst); + + hci_dev_unlock(hdev); +} + struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) { struct hci_conn *conn; @@ -287,6 +300,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) conn->auth_type = HCI_AT_GENERAL_BONDING; conn->io_capability = hdev->io_capability; conn->remote_auth = 0xff; + conn->key_type = 0xff; conn->power_save = 1; conn->disc_timeout = HCI_DISCONN_TIMEOUT; @@ -311,6 +325,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) setup_timer(&conn->disc_timer, hci_conn_timeout, (unsigned long)conn); setup_timer(&conn->idle_timer, hci_conn_idle, (unsigned long)conn); + setup_timer(&conn->auto_accept_timer, hci_conn_auto_accept, + (unsigned long) conn); atomic_set(&conn->refcnt, 0); @@ -341,6 +357,8 @@ int hci_conn_del(struct hci_conn *conn) del_timer(&conn->disc_timer); + del_timer(&conn->auto_accept_timer); + if (conn->type == ACL_LINK) { struct hci_conn *sco = conn->link; if (sco) @@ -535,36 +553,93 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) return 0; } +/* Encrypt the the link */ +static void hci_conn_encrypt(struct hci_conn *conn) +{ + BT_DBG("conn %p", conn); + + if (!test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) { + struct hci_cp_set_conn_encrypt cp; + cp.handle = cpu_to_le16(conn->handle); + cp.encrypt = 0x01; + hci_send_cmd(conn->hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp), + &cp); + } +} + /* Enable security */ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) { BT_DBG("conn %p", conn); + /* For sdp we don't need the link key. */ if (sec_level == BT_SECURITY_SDP) return 1; + /* For non 2.1 devices and low security level we don't need the link + key. */ if (sec_level == BT_SECURITY_LOW && (!conn->ssp_mode || !conn->hdev->ssp_mode)) return 1; - if (conn->link_mode & HCI_LM_ENCRYPT) - return hci_conn_auth(conn, sec_level, auth_type); - + /* For other security levels we need the link key. */ + if (!(conn->link_mode & HCI_LM_AUTH)) + goto auth; + + /* An authenticated combination key has sufficient security for any + security level. */ + if (conn->key_type == HCI_LK_AUTH_COMBINATION) + goto encrypt; + + /* An unauthenticated combination key has sufficient security for + security level 1 and 2. */ + if (conn->key_type == HCI_LK_UNAUTH_COMBINATION && + (sec_level == BT_SECURITY_MEDIUM || + sec_level == BT_SECURITY_LOW)) + goto encrypt; + + /* A combination key has always sufficient security for the security + levels 1 or 2. High security level requires the combination key + is generated using maximum PIN code length (16). + For pre 2.1 units. */ + if (conn->key_type == HCI_LK_COMBINATION && + (sec_level != BT_SECURITY_HIGH || + conn->pin_length == 16)) + goto encrypt; + +auth: if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) return 0; - if (hci_conn_auth(conn, sec_level, auth_type)) { - struct hci_cp_set_conn_encrypt cp; - cp.handle = cpu_to_le16(conn->handle); - cp.encrypt = 1; - hci_send_cmd(conn->hdev, HCI_OP_SET_CONN_ENCRYPT, - sizeof(cp), &cp); - } + hci_conn_auth(conn, sec_level, auth_type); + return 0; + +encrypt: + if (conn->link_mode & HCI_LM_ENCRYPT) + return 1; + hci_conn_encrypt(conn); return 0; } EXPORT_SYMBOL(hci_conn_security); +/* Check secure link requirement */ +int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level) +{ + BT_DBG("conn %p", conn); + + if (sec_level != BT_SECURITY_HIGH) + return 1; /* Accept if non-secure is required */ + + if (conn->key_type == HCI_LK_AUTH_COMBINATION || + (conn->key_type == HCI_LK_COMBINATION && + conn->pin_length == 16)) + return 1; + + return 0; /* Reject not secure link */ +} +EXPORT_SYMBOL(hci_conn_check_secure); + /* Change link key */ int hci_conn_change_link_key(struct hci_conn *conn) { diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 1ad4907766c..e937adab368 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -586,10 +586,8 @@ static int hci_dev_do_close(struct hci_dev *hdev) hci_req_cancel(hdev, ENODEV); hci_req_lock(hdev); - /* Stop timer, it might be running */ - del_timer_sync(&hdev->cmd_timer); - if (!test_and_clear_bit(HCI_UP, &hdev->flags)) { + del_timer_sync(&hdev->cmd_timer); hci_req_unlock(hdev); return 0; } @@ -628,6 +626,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) /* Drop last sent command */ if (hdev->sent_cmd) { + del_timer_sync(&hdev->cmd_timer); kfree_skb(hdev->sent_cmd); hdev->sent_cmd = NULL; } @@ -1021,18 +1020,54 @@ struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) return NULL; } -int hci_add_link_key(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr, - u8 *val, u8 type, u8 pin_len) +static int hci_persistent_key(struct hci_dev *hdev, struct hci_conn *conn, + u8 key_type, u8 old_key_type) +{ + /* Legacy key */ + if (key_type < 0x03) + return 1; + + /* Debug keys are insecure so don't store them persistently */ + if (key_type == HCI_LK_DEBUG_COMBINATION) + return 0; + + /* Changed combination key and there's no previous one */ + if (key_type == HCI_LK_CHANGED_COMBINATION && old_key_type == 0xff) + return 0; + + /* Security mode 3 case */ + if (!conn) + return 1; + + /* Neither local nor remote side had no-bonding as requirement */ + if (conn->auth_type > 0x01 && conn->remote_auth > 0x01) + return 1; + + /* Local side had dedicated bonding as requirement */ + if (conn->auth_type == 0x02 || conn->auth_type == 0x03) + return 1; + + /* Remote side had dedicated bonding as requirement */ + if (conn->remote_auth == 0x02 || conn->remote_auth == 0x03) + return 1; + + /* If none of the above criteria match, then don't store the key + * persistently */ + return 0; +} + +int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, + bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len) { struct link_key *key, *old_key; - u8 old_key_type; + u8 old_key_type, persistent; old_key = hci_find_link_key(hdev, bdaddr); if (old_key) { old_key_type = old_key->type; key = old_key; } else { - old_key_type = 0xff; + old_key_type = conn ? conn->key_type : 0xff; key = kzalloc(sizeof(*key), GFP_ATOMIC); if (!key) return -ENOMEM; @@ -1041,16 +1076,37 @@ int hci_add_link_key(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr, BT_DBG("%s key for %s type %u", hdev->name, batostr(bdaddr), type); + /* Some buggy controller combinations generate a changed + * combination key for legacy pairing even when there's no + * previous key */ + if (type == HCI_LK_CHANGED_COMBINATION && + (!conn || conn->remote_auth == 0xff) && + old_key_type == 0xff) { + type = HCI_LK_COMBINATION; + if (conn) + conn->key_type = type; + } + bacpy(&key->bdaddr, bdaddr); memcpy(key->val, val, 16); - key->type = type; key->pin_len = pin_len; - if (new_key) - mgmt_new_key(hdev->id, key, old_key_type); - - if (type == 0x06) + if (type == HCI_LK_CHANGED_COMBINATION) key->type = old_key_type; + else + key->type = type; + + if (!new_key) + return 0; + + persistent = hci_persistent_key(hdev, conn, type, old_key_type); + + mgmt_new_key(hdev->id, key, persistent); + + if (!persistent) { + list_del(&key->list); + kfree(key); + } return 0; } @@ -1463,7 +1519,7 @@ int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count) data += (count - rem); count = rem; - }; + } return rem; } @@ -1498,7 +1554,7 @@ int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count) data += (count - rem); count = rem; - }; + } return rem; } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 7a3398d9cd6..f13ddbf858b 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -56,7 +56,9 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb) if (status) return; - clear_bit(HCI_INQUIRY, &hdev->flags); + if (test_bit(HCI_MGMT, &hdev->flags) && + test_and_clear_bit(HCI_INQUIRY, &hdev->flags)) + mgmt_discovering(hdev->id, 0); hci_req_complete(hdev, HCI_OP_INQUIRY_CANCEL, status); @@ -72,7 +74,9 @@ static void hci_cc_exit_periodic_inq(struct hci_dev *hdev, struct sk_buff *skb) if (status) return; - clear_bit(HCI_INQUIRY, &hdev->flags); + if (test_bit(HCI_MGMT, &hdev->flags) && + test_and_clear_bit(HCI_INQUIRY, &hdev->flags)) + mgmt_discovering(hdev->id, 0); hci_conn_check_pending(hdev); } @@ -841,10 +845,14 @@ static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) if (status) { hci_req_complete(hdev, HCI_OP_INQUIRY, status); - hci_conn_check_pending(hdev); - } else - set_bit(HCI_INQUIRY, &hdev->flags); + return; + } + + if (test_bit(HCI_MGMT, &hdev->flags) && + !test_and_set_bit(HCI_INQUIRY, + &hdev->flags)) + mgmt_discovering(hdev->id, 1); } static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status) @@ -1013,12 +1021,19 @@ static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status) hci_dev_lock(hdev); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); - if (conn && hci_outgoing_auth_needed(hdev, conn)) { + if (!conn) + goto unlock; + + if (!hci_outgoing_auth_needed(hdev, conn)) + goto unlock; + + if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) { struct hci_cp_auth_requested cp; cp.handle = __cpu_to_le16(conn->handle); hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp); } +unlock: hci_dev_unlock(hdev); } @@ -1208,7 +1223,9 @@ static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff BT_DBG("%s status %d", hdev->name, status); - clear_bit(HCI_INQUIRY, &hdev->flags); + if (test_bit(HCI_MGMT, &hdev->flags) && + test_and_clear_bit(HCI_INQUIRY, &hdev->flags)) + mgmt_discovering(hdev->id, 0); hci_req_complete(hdev, HCI_OP_INQUIRY, status); @@ -1228,6 +1245,12 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff * hci_dev_lock(hdev); + if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags)) { + + if (test_bit(HCI_MGMT, &hdev->flags)) + mgmt_discovering(hdev->id, 1); + } + for (; num_rsp; num_rsp--, info++) { bacpy(&data.bdaddr, &info->bdaddr); data.pscan_rep_mode = info->pscan_rep_mode; @@ -1417,7 +1440,7 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff conn->state = BT_CLOSED; - if (conn->type == ACL_LINK) + if (conn->type == ACL_LINK || conn->type == LE_LINK) mgmt_disconnected(hdev->id, &conn->dst); hci_proto_disconn_cfm(conn, ev->reason); @@ -1443,7 +1466,6 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s conn->sec_level = conn->pending_sec_level; } else { mgmt_auth_failed(hdev->id, &conn->dst, ev->status); - conn->sec_level = BT_SECURITY_LOW; } clear_bit(HCI_CONN_AUTH_PEND, &conn->pend); @@ -1501,12 +1523,19 @@ static inline void hci_remote_name_evt(struct hci_dev *hdev, struct sk_buff *skb mgmt_remote_name(hdev->id, &ev->bdaddr, ev->name); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - if (conn && hci_outgoing_auth_needed(hdev, conn)) { + if (!conn) + goto unlock; + + if (!hci_outgoing_auth_needed(hdev, conn)) + goto unlock; + + if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) { struct hci_cp_auth_requested cp; cp.handle = __cpu_to_le16(conn->handle); hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp); } +unlock: hci_dev_unlock(hdev); } @@ -2006,9 +2035,16 @@ static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff if (!test_bit(HCI_PAIRABLE, &hdev->flags)) hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, sizeof(ev->bdaddr), &ev->bdaddr); + else if (test_bit(HCI_MGMT, &hdev->flags)) { + u8 secure; - if (test_bit(HCI_MGMT, &hdev->flags)) - mgmt_pin_code_request(hdev->id, &ev->bdaddr); + if (conn->pending_sec_level == BT_SECURITY_HIGH) + secure = 1; + else + secure = 0; + + mgmt_pin_code_request(hdev->id, &ev->bdaddr, secure); + } hci_dev_unlock(hdev); } @@ -2037,17 +2073,30 @@ static inline void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff BT_DBG("%s found key type %u for %s", hdev->name, key->type, batostr(&ev->bdaddr)); - if (!test_bit(HCI_DEBUG_KEYS, &hdev->flags) && key->type == 0x03) { + if (!test_bit(HCI_DEBUG_KEYS, &hdev->flags) && + key->type == HCI_LK_DEBUG_COMBINATION) { BT_DBG("%s ignoring debug key", hdev->name); goto not_found; } conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); + if (conn) { + if (key->type == HCI_LK_UNAUTH_COMBINATION && + conn->auth_type != 0xff && + (conn->auth_type & 0x01)) { + BT_DBG("%s ignoring unauthenticated key", hdev->name); + goto not_found; + } - if (key->type == 0x04 && conn && conn->auth_type != 0xff && - (conn->auth_type & 0x01)) { - BT_DBG("%s ignoring unauthenticated key", hdev->name); - goto not_found; + if (key->type == HCI_LK_COMBINATION && key->pin_len < 16 && + conn->pending_sec_level == BT_SECURITY_HIGH) { + BT_DBG("%s ignoring key unauthenticated for high \ + security", hdev->name); + goto not_found; + } + + conn->key_type = key->type; + conn->pin_length = key->pin_len; } bacpy(&cp.bdaddr, &ev->bdaddr); @@ -2079,11 +2128,15 @@ static inline void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff hci_conn_hold(conn); conn->disc_timeout = HCI_DISCONN_TIMEOUT; pin_len = conn->pin_length; + + if (ev->key_type != HCI_LK_CHANGED_COMBINATION) + conn->key_type = ev->key_type; + hci_conn_put(conn); } if (test_bit(HCI_LINK_KEYS, &hdev->flags)) - hci_add_link_key(hdev, 1, &ev->bdaddr, ev->link_key, + hci_add_link_key(hdev, conn, 1, &ev->bdaddr, ev->link_key, ev->key_type, pin_len); hci_dev_unlock(hdev); @@ -2158,6 +2211,12 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct hci_dev_lock(hdev); + if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags)) { + + if (test_bit(HCI_MGMT, &hdev->flags)) + mgmt_discovering(hdev->id, 1); + } + if ((skb->len - 1) / num_rsp != sizeof(struct inquiry_info_with_rssi)) { struct inquiry_info_with_rssi_and_pscan_mode *info; info = (void *) (skb->data + 1); @@ -2320,6 +2379,12 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct if (!num_rsp) return; + if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags)) { + + if (test_bit(HCI_MGMT, &hdev->flags)) + mgmt_discovering(hdev->id, 1); + } + hci_dev_lock(hdev); for (; num_rsp; num_rsp--, info++) { @@ -2353,7 +2418,7 @@ static inline u8 hci_get_auth_req(struct hci_conn *conn) /* If remote requests no-bonding follow that lead */ if (conn->remote_auth == 0x00 || conn->remote_auth == 0x01) - return 0x00; + return conn->remote_auth | (conn->auth_type & 0x01); return conn->auth_type; } @@ -2382,7 +2447,8 @@ static inline void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff bacpy(&cp.bdaddr, &ev->bdaddr); cp.capability = conn->io_capability; - cp.authentication = hci_get_auth_req(conn); + conn->auth_type = hci_get_auth_req(conn); + cp.authentication = conn->auth_type; if ((conn->out == 0x01 || conn->remote_oob == 0x01) && hci_find_remote_oob_data(hdev, &conn->dst)) @@ -2396,7 +2462,7 @@ static inline void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff struct hci_cp_io_capability_neg_reply cp; bacpy(&cp.bdaddr, &ev->bdaddr); - cp.reason = 0x16; /* Pairing not allowed */ + cp.reason = 0x18; /* Pairing not allowed */ hci_send_cmd(hdev, HCI_OP_IO_CAPABILITY_NEG_REPLY, sizeof(cp), &cp); @@ -2419,8 +2485,6 @@ static inline void hci_io_capa_reply_evt(struct hci_dev *hdev, struct sk_buff *s if (!conn) goto unlock; - hci_conn_hold(conn); - conn->remote_cap = ev->capability; conn->remote_oob = ev->oob_data; conn->remote_auth = ev->authentication; @@ -2433,14 +2497,67 @@ static inline void hci_user_confirm_request_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_user_confirm_req *ev = (void *) skb->data; + int loc_mitm, rem_mitm, confirm_hint = 0; + struct hci_conn *conn; BT_DBG("%s", hdev->name); hci_dev_lock(hdev); - if (test_bit(HCI_MGMT, &hdev->flags)) - mgmt_user_confirm_request(hdev->id, &ev->bdaddr, ev->passkey); + if (!test_bit(HCI_MGMT, &hdev->flags)) + goto unlock; + + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); + if (!conn) + goto unlock; + + loc_mitm = (conn->auth_type & 0x01); + rem_mitm = (conn->remote_auth & 0x01); + + /* If we require MITM but the remote device can't provide that + * (it has NoInputNoOutput) then reject the confirmation + * request. The only exception is when we're dedicated bonding + * initiators (connect_cfm_cb set) since then we always have the MITM + * bit set. */ + if (!conn->connect_cfm_cb && loc_mitm && conn->remote_cap == 0x03) { + BT_DBG("Rejecting request: remote device can't provide MITM"); + hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_NEG_REPLY, + sizeof(ev->bdaddr), &ev->bdaddr); + goto unlock; + } + /* If no side requires MITM protection; auto-accept */ + if ((!loc_mitm || conn->remote_cap == 0x03) && + (!rem_mitm || conn->io_capability == 0x03)) { + + /* If we're not the initiators request authorization to + * proceed from user space (mgmt_user_confirm with + * confirm_hint set to 1). */ + if (!test_bit(HCI_CONN_AUTH_PEND, &conn->pend)) { + BT_DBG("Confirming auto-accept as acceptor"); + confirm_hint = 1; + goto confirm; + } + + BT_DBG("Auto-accept of user confirmation with %ums delay", + hdev->auto_accept_delay); + + if (hdev->auto_accept_delay > 0) { + int delay = msecs_to_jiffies(hdev->auto_accept_delay); + mod_timer(&conn->auto_accept_timer, jiffies + delay); + goto unlock; + } + + hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_REPLY, + sizeof(ev->bdaddr), &ev->bdaddr); + goto unlock; + } + +confirm: + mgmt_user_confirm_request(hdev->id, &ev->bdaddr, ev->passkey, + confirm_hint); + +unlock: hci_dev_unlock(hdev); } @@ -2497,6 +2614,9 @@ static inline void hci_remote_oob_data_request_evt(struct hci_dev *hdev, hci_dev_lock(hdev); + if (!test_bit(HCI_MGMT, &hdev->flags)) + goto unlock; + data = hci_find_remote_oob_data(hdev, &ev->bdaddr); if (data) { struct hci_cp_remote_oob_data_reply cp; @@ -2515,6 +2635,7 @@ static inline void hci_remote_oob_data_request_evt(struct hci_dev *hdev, &cp); } +unlock: hci_dev_unlock(hdev); } @@ -2538,12 +2659,15 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff } if (ev->status) { + mgmt_connect_failed(hdev->id, &ev->bdaddr, ev->status); hci_proto_connect_cfm(conn, ev->status); conn->state = BT_CLOSED; hci_conn_del(conn); goto unlock; } + mgmt_connected(hdev->id, &ev->bdaddr); + conn->handle = __le16_to_cpu(ev->handle); conn->state = BT_CONNECTED; diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index 8775933ea83..a6c3aa8be1f 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -511,6 +511,35 @@ static const struct file_operations uuids_fops = { .release = single_release, }; +static int auto_accept_delay_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock_bh(hdev); + + hdev->auto_accept_delay = val; + + hci_dev_unlock_bh(hdev); + + return 0; +} + +static int auto_accept_delay_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock_bh(hdev); + + *val = hdev->auto_accept_delay; + + hci_dev_unlock_bh(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get, + auto_accept_delay_set, "%llu\n"); + int hci_register_sysfs(struct hci_dev *hdev) { struct device *dev = &hdev->dev; @@ -545,6 +574,8 @@ int hci_register_sysfs(struct hci_dev *hdev) debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops); + debugfs_create_file("auto_accept_delay", 0444, hdev->debugfs, hdev, + &auto_accept_delay_fops); return 0; } diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index ae6ebc6c348..c405a954a60 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -979,8 +979,10 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, bacpy(&session->bdaddr, &bt_sk(ctrl_sock->sk)->dst); - session->ctrl_mtu = min_t(uint, l2cap_pi(ctrl_sock->sk)->omtu, l2cap_pi(ctrl_sock->sk)->imtu); - session->intr_mtu = min_t(uint, l2cap_pi(intr_sock->sk)->omtu, l2cap_pi(intr_sock->sk)->imtu); + session->ctrl_mtu = min_t(uint, l2cap_pi(ctrl_sock->sk)->chan->omtu, + l2cap_pi(ctrl_sock->sk)->chan->imtu); + session->intr_mtu = min_t(uint, l2cap_pi(intr_sock->sk)->chan->omtu, + l2cap_pi(intr_sock->sk)->chan->imtu); BT_DBG("ctrl mtu %d intr mtu %d", session->ctrl_mtu, session->intr_mtu); diff --git a/net/bluetooth/hidp/hidp.h b/net/bluetooth/hidp/hidp.h index 12822cde4b4..19e95004b28 100644 --- a/net/bluetooth/hidp/hidp.h +++ b/net/bluetooth/hidp/hidp.h @@ -80,7 +80,7 @@ #define HIDP_VIRTUAL_CABLE_UNPLUG 0 #define HIDP_BOOT_PROTOCOL_MODE 1 #define HIDP_BLUETOOTH_VENDOR_ID 9 -#define HIDP_WAITING_FOR_RETURN 10 +#define HIDP_WAITING_FOR_RETURN 10 #define HIDP_WAITING_FOR_SEND_ACK 11 struct hidp_connadd_req { diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index c3cebed205c..e64a1c2df23 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -62,165 +62,233 @@ static u8 l2cap_fixed_chan[8] = { 0x02, }; static struct workqueue_struct *_busy_wq; -struct bt_sock_list l2cap_sk_list = { - .lock = __RW_LOCK_UNLOCKED(l2cap_sk_list.lock) -}; +LIST_HEAD(chan_list); +DEFINE_RWLOCK(chan_list_lock); static void l2cap_busy_work(struct work_struct *work); static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code, u8 ident, u16 dlen, void *data); +static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data); static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb); /* ---- L2CAP channels ---- */ -static struct sock *__l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, u16 cid) +static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16 cid) { - struct sock *s; - for (s = l->head; s; s = l2cap_pi(s)->next_c) { - if (l2cap_pi(s)->dcid == cid) - break; + struct l2cap_chan *c; + + list_for_each_entry(c, &conn->chan_l, list) { + if (c->dcid == cid) + return c; } - return s; + return NULL; + } -static struct sock *__l2cap_get_chan_by_scid(struct l2cap_chan_list *l, u16 cid) +static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid) { - struct sock *s; - for (s = l->head; s; s = l2cap_pi(s)->next_c) { - if (l2cap_pi(s)->scid == cid) - break; + struct l2cap_chan *c; + + list_for_each_entry(c, &conn->chan_l, list) { + if (c->scid == cid) + return c; } - return s; + return NULL; } /* Find channel with given SCID. * Returns locked socket */ -static inline struct sock *l2cap_get_chan_by_scid(struct l2cap_chan_list *l, u16 cid) +static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid) { - struct sock *s; - read_lock(&l->lock); - s = __l2cap_get_chan_by_scid(l, cid); - if (s) - bh_lock_sock(s); - read_unlock(&l->lock); - return s; + struct l2cap_chan *c; + + read_lock(&conn->chan_lock); + c = __l2cap_get_chan_by_scid(conn, cid); + if (c) + bh_lock_sock(c->sk); + read_unlock(&conn->chan_lock); + return c; } -static struct sock *__l2cap_get_chan_by_ident(struct l2cap_chan_list *l, u8 ident) +static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident) { - struct sock *s; - for (s = l->head; s; s = l2cap_pi(s)->next_c) { - if (l2cap_pi(s)->ident == ident) - break; + struct l2cap_chan *c; + + list_for_each_entry(c, &conn->chan_l, list) { + if (c->ident == ident) + return c; } - return s; + return NULL; } -static inline struct sock *l2cap_get_chan_by_ident(struct l2cap_chan_list *l, u8 ident) +static inline struct l2cap_chan *l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident) { - struct sock *s; - read_lock(&l->lock); - s = __l2cap_get_chan_by_ident(l, ident); - if (s) - bh_lock_sock(s); - read_unlock(&l->lock); - return s; + struct l2cap_chan *c; + + read_lock(&conn->chan_lock); + c = __l2cap_get_chan_by_ident(conn, ident); + if (c) + bh_lock_sock(c->sk); + read_unlock(&conn->chan_lock); + return c; +} + +static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src) +{ + struct l2cap_chan *c; + + list_for_each_entry(c, &chan_list, global_l) { + if (c->sport == psm && !bacmp(&bt_sk(c->sk)->src, src)) + goto found; + } + + c = NULL; +found: + return c; +} + +int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm) +{ + int err; + + write_lock_bh(&chan_list_lock); + + if (psm && __l2cap_global_chan_by_addr(psm, src)) { + err = -EADDRINUSE; + goto done; + } + + if (psm) { + chan->psm = psm; + chan->sport = psm; + err = 0; + } else { + u16 p; + + err = -EINVAL; + for (p = 0x1001; p < 0x1100; p += 2) + if (!__l2cap_global_chan_by_addr(cpu_to_le16(p), src)) { + chan->psm = cpu_to_le16(p); + chan->sport = cpu_to_le16(p); + err = 0; + break; + } + } + +done: + write_unlock_bh(&chan_list_lock); + return err; +} + +int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid) +{ + write_lock_bh(&chan_list_lock); + + chan->scid = scid; + + write_unlock_bh(&chan_list_lock); + + return 0; } -static u16 l2cap_alloc_cid(struct l2cap_chan_list *l) +static u16 l2cap_alloc_cid(struct l2cap_conn *conn) { u16 cid = L2CAP_CID_DYN_START; for (; cid < L2CAP_CID_DYN_END; cid++) { - if (!__l2cap_get_chan_by_scid(l, cid)) + if (!__l2cap_get_chan_by_scid(conn, cid)) return cid; } return 0; } -static inline void __l2cap_chan_link(struct l2cap_chan_list *l, struct sock *sk) +struct l2cap_chan *l2cap_chan_create(struct sock *sk) { - sock_hold(sk); + struct l2cap_chan *chan; - if (l->head) - l2cap_pi(l->head)->prev_c = sk; + chan = kzalloc(sizeof(*chan), GFP_ATOMIC); + if (!chan) + return NULL; - l2cap_pi(sk)->next_c = l->head; - l2cap_pi(sk)->prev_c = NULL; - l->head = sk; -} + chan->sk = sk; -static inline void l2cap_chan_unlink(struct l2cap_chan_list *l, struct sock *sk) -{ - struct sock *next = l2cap_pi(sk)->next_c, *prev = l2cap_pi(sk)->prev_c; + write_lock_bh(&chan_list_lock); + list_add(&chan->global_l, &chan_list); + write_unlock_bh(&chan_list_lock); - write_lock_bh(&l->lock); - if (sk == l->head) - l->head = next; + return chan; +} - if (next) - l2cap_pi(next)->prev_c = prev; - if (prev) - l2cap_pi(prev)->next_c = next; - write_unlock_bh(&l->lock); +void l2cap_chan_destroy(struct l2cap_chan *chan) +{ + write_lock_bh(&chan_list_lock); + list_del(&chan->global_l); + write_unlock_bh(&chan_list_lock); - __sock_put(sk); + kfree(chan); } -static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk) +static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) { - struct l2cap_chan_list *l = &conn->chan_list; + struct sock *sk = chan->sk; BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, - l2cap_pi(sk)->psm, l2cap_pi(sk)->dcid); + chan->psm, chan->dcid); conn->disc_reason = 0x13; - l2cap_pi(sk)->conn = conn; + chan->conn = conn; if (sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM) { if (conn->hcon->type == LE_LINK) { /* LE connection */ - l2cap_pi(sk)->omtu = L2CAP_LE_DEFAULT_MTU; - l2cap_pi(sk)->scid = L2CAP_CID_LE_DATA; - l2cap_pi(sk)->dcid = L2CAP_CID_LE_DATA; + chan->omtu = L2CAP_LE_DEFAULT_MTU; + chan->scid = L2CAP_CID_LE_DATA; + chan->dcid = L2CAP_CID_LE_DATA; } else { /* Alloc CID for connection-oriented socket */ - l2cap_pi(sk)->scid = l2cap_alloc_cid(l); - l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU; + chan->scid = l2cap_alloc_cid(conn); + chan->omtu = L2CAP_DEFAULT_MTU; } } else if (sk->sk_type == SOCK_DGRAM) { /* Connectionless socket */ - l2cap_pi(sk)->scid = L2CAP_CID_CONN_LESS; - l2cap_pi(sk)->dcid = L2CAP_CID_CONN_LESS; - l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU; + chan->scid = L2CAP_CID_CONN_LESS; + chan->dcid = L2CAP_CID_CONN_LESS; + chan->omtu = L2CAP_DEFAULT_MTU; } else { /* Raw socket can send/recv signalling messages only */ - l2cap_pi(sk)->scid = L2CAP_CID_SIGNALING; - l2cap_pi(sk)->dcid = L2CAP_CID_SIGNALING; - l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU; + chan->scid = L2CAP_CID_SIGNALING; + chan->dcid = L2CAP_CID_SIGNALING; + chan->omtu = L2CAP_DEFAULT_MTU; } - __l2cap_chan_link(l, sk); + sock_hold(sk); + + list_add(&chan->list, &conn->chan_l); } /* Delete channel. * Must be called on the locked socket. */ -void l2cap_chan_del(struct sock *sk, int err) +void l2cap_chan_del(struct l2cap_chan *chan, int err) { - struct l2cap_conn *conn = l2cap_pi(sk)->conn; + struct sock *sk = chan->sk; + struct l2cap_conn *conn = chan->conn; struct sock *parent = bt_sk(sk)->parent; l2cap_sock_clear_timer(sk); - BT_DBG("sk %p, conn %p, err %d", sk, conn, err); + BT_DBG("chan %p, conn %p, err %d", chan, conn, err); if (conn) { - /* Unlink from channel list */ - l2cap_chan_unlink(&conn->chan_list, sk); - l2cap_pi(sk)->conn = NULL; + /* Delete from channel list */ + write_lock_bh(&conn->chan_lock); + list_del(&chan->list); + write_unlock_bh(&conn->chan_lock); + __sock_put(sk); + + chan->conn = NULL; hci_conn_put(conn->hcon); } @@ -236,29 +304,35 @@ void l2cap_chan_del(struct sock *sk, int err) } else sk->sk_state_change(sk); - skb_queue_purge(TX_QUEUE(sk)); + if (!(chan->conf_state & L2CAP_CONF_OUTPUT_DONE && + chan->conf_state & L2CAP_CONF_INPUT_DONE)) + return; + + skb_queue_purge(&chan->tx_q); - if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) { + if (chan->mode == L2CAP_MODE_ERTM) { struct srej_list *l, *tmp; - del_timer(&l2cap_pi(sk)->retrans_timer); - del_timer(&l2cap_pi(sk)->monitor_timer); - del_timer(&l2cap_pi(sk)->ack_timer); + del_timer(&chan->retrans_timer); + del_timer(&chan->monitor_timer); + del_timer(&chan->ack_timer); - skb_queue_purge(SREJ_QUEUE(sk)); - skb_queue_purge(BUSY_QUEUE(sk)); + skb_queue_purge(&chan->srej_q); + skb_queue_purge(&chan->busy_q); - list_for_each_entry_safe(l, tmp, SREJ_LIST(sk), list) { + list_for_each_entry_safe(l, tmp, &chan->srej_l, list) { list_del(&l->list); kfree(l); } } } -static inline u8 l2cap_get_auth_type(struct sock *sk) +static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan) { + struct sock *sk = chan->sk; + if (sk->sk_type == SOCK_RAW) { - switch (l2cap_pi(sk)->sec_level) { + switch (chan->sec_level) { case BT_SECURITY_HIGH: return HCI_AT_DEDICATED_BONDING_MITM; case BT_SECURITY_MEDIUM: @@ -266,16 +340,16 @@ static inline u8 l2cap_get_auth_type(struct sock *sk) default: return HCI_AT_NO_BONDING; } - } else if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) { - if (l2cap_pi(sk)->sec_level == BT_SECURITY_LOW) - l2cap_pi(sk)->sec_level = BT_SECURITY_SDP; + } else if (chan->psm == cpu_to_le16(0x0001)) { + if (chan->sec_level == BT_SECURITY_LOW) + chan->sec_level = BT_SECURITY_SDP; - if (l2cap_pi(sk)->sec_level == BT_SECURITY_HIGH) + if (chan->sec_level == BT_SECURITY_HIGH) return HCI_AT_NO_BONDING_MITM; else return HCI_AT_NO_BONDING; } else { - switch (l2cap_pi(sk)->sec_level) { + switch (chan->sec_level) { case BT_SECURITY_HIGH: return HCI_AT_GENERAL_BONDING_MITM; case BT_SECURITY_MEDIUM: @@ -287,15 +361,14 @@ static inline u8 l2cap_get_auth_type(struct sock *sk) } /* Service level security */ -static inline int l2cap_check_security(struct sock *sk) +static inline int l2cap_check_security(struct l2cap_chan *chan) { - struct l2cap_conn *conn = l2cap_pi(sk)->conn; + struct l2cap_conn *conn = chan->conn; __u8 auth_type; - auth_type = l2cap_get_auth_type(sk); + auth_type = l2cap_get_auth_type(chan); - return hci_conn_security(conn->hcon, l2cap_pi(sk)->sec_level, - auth_type); + return hci_conn_security(conn->hcon, chan->sec_level, auth_type); } u8 l2cap_get_ident(struct l2cap_conn *conn) @@ -338,11 +411,12 @@ void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *d hci_send_acl(conn->hcon, skb, flags); } -static inline void l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control) +static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control) { struct sk_buff *skb; struct l2cap_hdr *lh; - struct l2cap_conn *conn = pi->conn; + struct l2cap_pinfo *pi = l2cap_pi(chan->sk); + struct l2cap_conn *conn = chan->conn; struct sock *sk = (struct sock *)pi; int count, hlen = L2CAP_HDR_SIZE + 2; u8 flags; @@ -350,22 +424,22 @@ static inline void l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control) if (sk->sk_state != BT_CONNECTED) return; - if (pi->fcs == L2CAP_FCS_CRC16) + if (chan->fcs == L2CAP_FCS_CRC16) hlen += 2; - BT_DBG("pi %p, control 0x%2.2x", pi, control); + BT_DBG("chan %p, control 0x%2.2x", chan, control); count = min_t(unsigned int, conn->mtu, hlen); control |= L2CAP_CTRL_FRAME_TYPE; - if (pi->conn_state & L2CAP_CONN_SEND_FBIT) { + if (chan->conn_state & L2CAP_CONN_SEND_FBIT) { control |= L2CAP_CTRL_FINAL; - pi->conn_state &= ~L2CAP_CONN_SEND_FBIT; + chan->conn_state &= ~L2CAP_CONN_SEND_FBIT; } - if (pi->conn_state & L2CAP_CONN_SEND_PBIT) { + if (chan->conn_state & L2CAP_CONN_SEND_PBIT) { control |= L2CAP_CTRL_POLL; - pi->conn_state &= ~L2CAP_CONN_SEND_PBIT; + chan->conn_state &= ~L2CAP_CONN_SEND_PBIT; } skb = bt_skb_alloc(count, GFP_ATOMIC); @@ -374,10 +448,10 @@ static inline void l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control) lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh->len = cpu_to_le16(hlen - L2CAP_HDR_SIZE); - lh->cid = cpu_to_le16(pi->dcid); + lh->cid = cpu_to_le16(chan->dcid); put_unaligned_le16(control, skb_put(skb, 2)); - if (pi->fcs == L2CAP_FCS_CRC16) { + if (chan->fcs == L2CAP_FCS_CRC16) { u16 fcs = crc16(0, (u8 *)lh, count - 2); put_unaligned_le16(fcs, skb_put(skb, 2)); } @@ -387,45 +461,46 @@ static inline void l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control) else flags = ACL_START; - hci_send_acl(pi->conn->hcon, skb, flags); + hci_send_acl(chan->conn->hcon, skb, flags); } -static inline void l2cap_send_rr_or_rnr(struct l2cap_pinfo *pi, u16 control) +static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u16 control) { - if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY) { + if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) { control |= L2CAP_SUPER_RCV_NOT_READY; - pi->conn_state |= L2CAP_CONN_RNR_SENT; + chan->conn_state |= L2CAP_CONN_RNR_SENT; } else control |= L2CAP_SUPER_RCV_READY; - control |= pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; + control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; - l2cap_send_sframe(pi, control); + l2cap_send_sframe(chan, control); } -static inline int __l2cap_no_conn_pending(struct sock *sk) +static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan) { - return !(l2cap_pi(sk)->conf_state & L2CAP_CONF_CONNECT_PEND); + return !(chan->conf_state & L2CAP_CONF_CONNECT_PEND); } -static void l2cap_do_start(struct sock *sk) +static void l2cap_do_start(struct l2cap_chan *chan) { - struct l2cap_conn *conn = l2cap_pi(sk)->conn; + struct l2cap_conn *conn = chan->conn; if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) { if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE)) return; - if (l2cap_check_security(sk) && __l2cap_no_conn_pending(sk)) { + if (l2cap_check_security(chan) && + __l2cap_no_conn_pending(chan)) { struct l2cap_conn_req req; - req.scid = cpu_to_le16(l2cap_pi(sk)->scid); - req.psm = l2cap_pi(sk)->psm; + req.scid = cpu_to_le16(chan->scid); + req.psm = chan->psm; - l2cap_pi(sk)->ident = l2cap_get_ident(conn); - l2cap_pi(sk)->conf_state |= L2CAP_CONF_CONNECT_PEND; + chan->ident = l2cap_get_ident(conn); + chan->conf_state |= L2CAP_CONF_CONNECT_PEND; - l2cap_send_cmd(conn, l2cap_pi(sk)->ident, - L2CAP_CONN_REQ, sizeof(req), &req); + l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, + sizeof(req), &req); } } else { struct l2cap_info_req req; @@ -458,23 +533,24 @@ static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask) } } -void l2cap_send_disconn_req(struct l2cap_conn *conn, struct sock *sk, int err) +void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *chan, int err) { + struct sock *sk; struct l2cap_disconn_req req; if (!conn) return; - skb_queue_purge(TX_QUEUE(sk)); + sk = chan->sk; - if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) { - del_timer(&l2cap_pi(sk)->retrans_timer); - del_timer(&l2cap_pi(sk)->monitor_timer); - del_timer(&l2cap_pi(sk)->ack_timer); + if (chan->mode == L2CAP_MODE_ERTM) { + del_timer(&chan->retrans_timer); + del_timer(&chan->monitor_timer); + del_timer(&chan->ack_timer); } - req.dcid = cpu_to_le16(l2cap_pi(sk)->dcid); - req.scid = cpu_to_le16(l2cap_pi(sk)->scid); + req.dcid = cpu_to_le16(chan->dcid); + req.scid = cpu_to_le16(chan->scid); l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_DISCONN_REQ, sizeof(req), &req); @@ -485,17 +561,15 @@ void l2cap_send_disconn_req(struct l2cap_conn *conn, struct sock *sk, int err) /* ---- L2CAP connections ---- */ static void l2cap_conn_start(struct l2cap_conn *conn) { - struct l2cap_chan_list *l = &conn->chan_list; - struct sock_del_list del, *tmp1, *tmp2; - struct sock *sk; + struct l2cap_chan *chan, *tmp; BT_DBG("conn %p", conn); - INIT_LIST_HEAD(&del.list); + read_lock(&conn->chan_lock); - read_lock(&l->lock); + list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) { + struct sock *sk = chan->sk; - for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { bh_lock_sock(sk); if (sk->sk_type != SOCK_SEQPACKET && @@ -507,40 +581,41 @@ static void l2cap_conn_start(struct l2cap_conn *conn) if (sk->sk_state == BT_CONNECT) { struct l2cap_conn_req req; - if (!l2cap_check_security(sk) || - !__l2cap_no_conn_pending(sk)) { + if (!l2cap_check_security(chan) || + !__l2cap_no_conn_pending(chan)) { bh_unlock_sock(sk); continue; } - if (!l2cap_mode_supported(l2cap_pi(sk)->mode, + if (!l2cap_mode_supported(chan->mode, conn->feat_mask) - && l2cap_pi(sk)->conf_state & + && chan->conf_state & L2CAP_CONF_STATE2_DEVICE) { - tmp1 = kzalloc(sizeof(struct sock_del_list), - GFP_ATOMIC); - tmp1->sk = sk; - list_add_tail(&tmp1->list, &del.list); + /* __l2cap_sock_close() calls list_del(chan) + * so release the lock */ + read_unlock_bh(&conn->chan_lock); + __l2cap_sock_close(sk, ECONNRESET); + read_lock_bh(&conn->chan_lock); bh_unlock_sock(sk); continue; } - req.scid = cpu_to_le16(l2cap_pi(sk)->scid); - req.psm = l2cap_pi(sk)->psm; + req.scid = cpu_to_le16(chan->scid); + req.psm = chan->psm; - l2cap_pi(sk)->ident = l2cap_get_ident(conn); - l2cap_pi(sk)->conf_state |= L2CAP_CONF_CONNECT_PEND; + chan->ident = l2cap_get_ident(conn); + chan->conf_state |= L2CAP_CONF_CONNECT_PEND; - l2cap_send_cmd(conn, l2cap_pi(sk)->ident, - L2CAP_CONN_REQ, sizeof(req), &req); + l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, + sizeof(req), &req); } else if (sk->sk_state == BT_CONNECT2) { struct l2cap_conn_rsp rsp; char buf[128]; - rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); - rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); + rsp.scid = cpu_to_le16(chan->dcid); + rsp.dcid = cpu_to_le16(chan->scid); - if (l2cap_check_security(sk)) { + if (l2cap_check_security(chan)) { if (bt_sk(sk)->defer_setup) { struct sock *parent = bt_sk(sk)->parent; rsp.result = cpu_to_le16(L2CAP_CR_PEND); @@ -557,80 +632,77 @@ static void l2cap_conn_start(struct l2cap_conn *conn) rsp.status = cpu_to_le16(L2CAP_CS_AUTHEN_PEND); } - l2cap_send_cmd(conn, l2cap_pi(sk)->ident, - L2CAP_CONN_RSP, sizeof(rsp), &rsp); + l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, + sizeof(rsp), &rsp); - if (l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT || + if (chan->conf_state & L2CAP_CONF_REQ_SENT || rsp.result != L2CAP_CR_SUCCESS) { bh_unlock_sock(sk); continue; } - l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT; + chan->conf_state |= L2CAP_CONF_REQ_SENT; l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, - l2cap_build_conf_req(sk, buf), buf); - l2cap_pi(sk)->num_conf_req++; + l2cap_build_conf_req(chan, buf), buf); + chan->num_conf_req++; } bh_unlock_sock(sk); } - read_unlock(&l->lock); - - list_for_each_entry_safe(tmp1, tmp2, &del.list, list) { - bh_lock_sock(tmp1->sk); - __l2cap_sock_close(tmp1->sk, ECONNRESET); - bh_unlock_sock(tmp1->sk); - list_del(&tmp1->list); - kfree(tmp1); - } + read_unlock(&conn->chan_lock); } /* Find socket with cid and source bdaddr. * Returns closest match, locked. */ -static struct sock *l2cap_get_sock_by_scid(int state, __le16 cid, bdaddr_t *src) +static struct l2cap_chan *l2cap_global_chan_by_scid(int state, __le16 cid, bdaddr_t *src) { - struct sock *s, *sk = NULL, *sk1 = NULL; - struct hlist_node *node; + struct l2cap_chan *c, *c1 = NULL; + + read_lock(&chan_list_lock); - read_lock(&l2cap_sk_list.lock); + list_for_each_entry(c, &chan_list, global_l) { + struct sock *sk = c->sk; - sk_for_each(sk, node, &l2cap_sk_list.head) { if (state && sk->sk_state != state) continue; - if (l2cap_pi(sk)->scid == cid) { + if (c->scid == cid) { /* Exact match. */ - if (!bacmp(&bt_sk(sk)->src, src)) - break; + if (!bacmp(&bt_sk(sk)->src, src)) { + read_unlock(&chan_list_lock); + return c; + } /* Closest match */ if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) - sk1 = sk; + c1 = c; } } - s = node ? sk : sk1; - if (s) - bh_lock_sock(s); - read_unlock(&l2cap_sk_list.lock); - return s; + read_unlock(&chan_list_lock); + + return c1; } static void l2cap_le_conn_ready(struct l2cap_conn *conn) { - struct l2cap_chan_list *list = &conn->chan_list; - struct sock *parent, *uninitialized_var(sk); + struct sock *parent, *sk; + struct l2cap_chan *chan, *pchan; BT_DBG(""); /* Check if we have socket listening on cid */ - parent = l2cap_get_sock_by_scid(BT_LISTEN, L2CAP_CID_LE_DATA, + pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_LE_DATA, conn->src); - if (!parent) + if (!pchan) return; + parent = pchan->sk; + + bh_lock_sock(parent); + /* Check for backlog size */ if (sk_acceptq_is_full(parent)) { BT_DBG("backlog full %d", parent->sk_ack_backlog); @@ -641,24 +713,33 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) if (!sk) goto clean; - write_lock_bh(&list->lock); + chan = l2cap_chan_create(sk); + if (!chan) { + l2cap_sock_kill(sk); + goto clean; + } + + l2cap_pi(sk)->chan = chan; + + write_lock_bh(&conn->chan_lock); hci_conn_hold(conn->hcon); l2cap_sock_init(sk, parent); + bacpy(&bt_sk(sk)->src, conn->src); bacpy(&bt_sk(sk)->dst, conn->dst); bt_accept_enqueue(parent, sk); - __l2cap_chan_add(conn, sk); + __l2cap_chan_add(conn, chan); l2cap_sock_set_timer(sk, sk->sk_sndtimeo); sk->sk_state = BT_CONNECTED; parent->sk_data_ready(parent, 0); - write_unlock_bh(&list->lock); + write_unlock_bh(&conn->chan_lock); clean: bh_unlock_sock(parent); @@ -666,17 +747,18 @@ clean: static void l2cap_conn_ready(struct l2cap_conn *conn) { - struct l2cap_chan_list *l = &conn->chan_list; - struct sock *sk; + struct l2cap_chan *chan; BT_DBG("conn %p", conn); if (!conn->hcon->out && conn->hcon->type == LE_LINK) l2cap_le_conn_ready(conn); - read_lock(&l->lock); + read_lock(&conn->chan_lock); + + list_for_each_entry(chan, &conn->chan_l, list) { + struct sock *sk = chan->sk; - for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { bh_lock_sock(sk); if (conn->hcon->type == LE_LINK) { @@ -691,30 +773,31 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) sk->sk_state = BT_CONNECTED; sk->sk_state_change(sk); } else if (sk->sk_state == BT_CONNECT) - l2cap_do_start(sk); + l2cap_do_start(chan); bh_unlock_sock(sk); } - read_unlock(&l->lock); + read_unlock(&conn->chan_lock); } /* Notify sockets that we cannot guaranty reliability anymore */ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err) { - struct l2cap_chan_list *l = &conn->chan_list; - struct sock *sk; + struct l2cap_chan *chan; BT_DBG("conn %p", conn); - read_lock(&l->lock); + read_lock(&conn->chan_lock); - for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { - if (l2cap_pi(sk)->force_reliable) + list_for_each_entry(chan, &conn->chan_l, list) { + struct sock *sk = chan->sk; + + if (chan->force_reliable) sk->sk_err = err; } - read_unlock(&l->lock); + read_unlock(&conn->chan_lock); } static void l2cap_info_timeout(unsigned long arg) @@ -754,7 +837,9 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) conn->feat_mask = 0; spin_lock_init(&conn->lock); - rwlock_init(&conn->chan_list.lock); + rwlock_init(&conn->chan_lock); + + INIT_LIST_HEAD(&conn->chan_l); if (hcon->type != LE_LINK) setup_timer(&conn->info_timer, l2cap_info_timeout, @@ -768,6 +853,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) static void l2cap_conn_del(struct hci_conn *hcon, int err) { struct l2cap_conn *conn = hcon->l2cap_data; + struct l2cap_chan *chan, *l; struct sock *sk; if (!conn) @@ -778,9 +864,10 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) kfree_skb(conn->rx_skb); /* Kill channels */ - while ((sk = conn->chan_list.head)) { + list_for_each_entry_safe(chan, l, &conn->chan_l, list) { + sk = chan->sk; bh_lock_sock(sk); - l2cap_chan_del(sk, err); + l2cap_chan_del(chan, err); bh_unlock_sock(sk); l2cap_sock_kill(sk); } @@ -792,12 +879,11 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) kfree(conn); } -static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk) +static inline void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) { - struct l2cap_chan_list *l = &conn->chan_list; - write_lock_bh(&l->lock); - __l2cap_chan_add(conn, sk); - write_unlock_bh(&l->lock); + write_lock_bh(&conn->chan_lock); + __l2cap_chan_add(conn, chan); + write_unlock_bh(&conn->chan_lock); } /* ---- Socket interface ---- */ @@ -805,35 +891,39 @@ static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk) /* Find socket with psm and source bdaddr. * Returns closest match. */ -static struct sock *l2cap_get_sock_by_psm(int state, __le16 psm, bdaddr_t *src) +static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, bdaddr_t *src) { - struct sock *sk = NULL, *sk1 = NULL; - struct hlist_node *node; + struct l2cap_chan *c, *c1 = NULL; + + read_lock(&chan_list_lock); - read_lock(&l2cap_sk_list.lock); + list_for_each_entry(c, &chan_list, global_l) { + struct sock *sk = c->sk; - sk_for_each(sk, node, &l2cap_sk_list.head) { if (state && sk->sk_state != state) continue; - if (l2cap_pi(sk)->psm == psm) { + if (c->psm == psm) { /* Exact match. */ - if (!bacmp(&bt_sk(sk)->src, src)) - break; + if (!bacmp(&bt_sk(sk)->src, src)) { + read_unlock(&chan_list_lock); + return c; + } /* Closest match */ if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) - sk1 = sk; + c1 = c; } } - read_unlock(&l2cap_sk_list.lock); + read_unlock(&chan_list_lock); - return node ? sk : sk1; + return c1; } -int l2cap_do_connect(struct sock *sk) +int l2cap_chan_connect(struct l2cap_chan *chan) { + struct sock *sk = chan->sk; bdaddr_t *src = &bt_sk(sk)->src; bdaddr_t *dst = &bt_sk(sk)->dst; struct l2cap_conn *conn; @@ -843,7 +933,7 @@ int l2cap_do_connect(struct sock *sk) int err; BT_DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst), - l2cap_pi(sk)->psm); + chan->psm); hdev = hci_get_route(dst, src); if (!hdev) @@ -851,14 +941,14 @@ int l2cap_do_connect(struct sock *sk) hci_dev_lock_bh(hdev); - auth_type = l2cap_get_auth_type(sk); + auth_type = l2cap_get_auth_type(chan); - if (l2cap_pi(sk)->dcid == L2CAP_CID_LE_DATA) + if (chan->dcid == L2CAP_CID_LE_DATA) hcon = hci_connect(hdev, LE_LINK, dst, - l2cap_pi(sk)->sec_level, auth_type); + chan->sec_level, auth_type); else hcon = hci_connect(hdev, ACL_LINK, dst, - l2cap_pi(sk)->sec_level, auth_type); + chan->sec_level, auth_type); if (IS_ERR(hcon)) { err = PTR_ERR(hcon); @@ -875,7 +965,7 @@ int l2cap_do_connect(struct sock *sk) /* Update source addr of the socket */ bacpy(src, conn->src); - l2cap_chan_add(conn, sk); + l2cap_chan_add(conn, chan); sk->sk_state = BT_CONNECT; l2cap_sock_set_timer(sk, sk->sk_sndtimeo); @@ -884,10 +974,10 @@ int l2cap_do_connect(struct sock *sk) if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM) { l2cap_sock_clear_timer(sk); - if (l2cap_check_security(sk)) + if (l2cap_check_security(chan)) sk->sk_state = BT_CONNECTED; } else - l2cap_do_start(sk); + l2cap_do_start(chan); } err = 0; @@ -900,12 +990,13 @@ done: int __l2cap_wait_ack(struct sock *sk) { + struct l2cap_chan *chan = l2cap_pi(sk)->chan; DECLARE_WAITQUEUE(wait, current); int err = 0; int timeo = HZ/5; add_wait_queue(sk_sleep(sk), &wait); - while ((l2cap_pi(sk)->unacked_frames > 0 && l2cap_pi(sk)->conn)) { + while ((chan->unacked_frames > 0 && chan->conn)) { set_current_state(TASK_INTERRUPTIBLE); if (!timeo) @@ -931,68 +1022,69 @@ int __l2cap_wait_ack(struct sock *sk) static void l2cap_monitor_timeout(unsigned long arg) { - struct sock *sk = (void *) arg; + struct l2cap_chan *chan = (void *) arg; + struct sock *sk = chan->sk; - BT_DBG("sk %p", sk); + BT_DBG("chan %p", chan); bh_lock_sock(sk); - if (l2cap_pi(sk)->retry_count >= l2cap_pi(sk)->remote_max_tx) { - l2cap_send_disconn_req(l2cap_pi(sk)->conn, sk, ECONNABORTED); + if (chan->retry_count >= chan->remote_max_tx) { + l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED); bh_unlock_sock(sk); return; } - l2cap_pi(sk)->retry_count++; + chan->retry_count++; __mod_monitor_timer(); - l2cap_send_rr_or_rnr(l2cap_pi(sk), L2CAP_CTRL_POLL); + l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL); bh_unlock_sock(sk); } static void l2cap_retrans_timeout(unsigned long arg) { - struct sock *sk = (void *) arg; + struct l2cap_chan *chan = (void *) arg; + struct sock *sk = chan->sk; - BT_DBG("sk %p", sk); + BT_DBG("chan %p", chan); bh_lock_sock(sk); - l2cap_pi(sk)->retry_count = 1; + chan->retry_count = 1; __mod_monitor_timer(); - l2cap_pi(sk)->conn_state |= L2CAP_CONN_WAIT_F; + chan->conn_state |= L2CAP_CONN_WAIT_F; - l2cap_send_rr_or_rnr(l2cap_pi(sk), L2CAP_CTRL_POLL); + l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL); bh_unlock_sock(sk); } -static void l2cap_drop_acked_frames(struct sock *sk) +static void l2cap_drop_acked_frames(struct l2cap_chan *chan) { struct sk_buff *skb; - while ((skb = skb_peek(TX_QUEUE(sk))) && - l2cap_pi(sk)->unacked_frames) { - if (bt_cb(skb)->tx_seq == l2cap_pi(sk)->expected_ack_seq) + while ((skb = skb_peek(&chan->tx_q)) && + chan->unacked_frames) { + if (bt_cb(skb)->tx_seq == chan->expected_ack_seq) break; - skb = skb_dequeue(TX_QUEUE(sk)); + skb = skb_dequeue(&chan->tx_q); kfree_skb(skb); - l2cap_pi(sk)->unacked_frames--; + chan->unacked_frames--; } - if (!l2cap_pi(sk)->unacked_frames) - del_timer(&l2cap_pi(sk)->retrans_timer); + if (!chan->unacked_frames) + del_timer(&chan->retrans_timer); } -void l2cap_do_send(struct sock *sk, struct sk_buff *skb) +void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) { - struct l2cap_pinfo *pi = l2cap_pi(sk); - struct hci_conn *hcon = pi->conn->hcon; + struct hci_conn *hcon = chan->conn->hcon; u16 flags; - BT_DBG("sk %p, skb %p len %d", sk, skb, skb->len); + BT_DBG("chan %p, skb %p len %d", chan, skb, skb->len); - if (!pi->flushable && lmp_no_flush_capable(hcon->hdev)) + if (!chan->flushable && lmp_no_flush_capable(hcon->hdev)) flags = ACL_START_NO_FLUSH; else flags = ACL_START; @@ -1000,35 +1092,33 @@ void l2cap_do_send(struct sock *sk, struct sk_buff *skb) hci_send_acl(hcon, skb, flags); } -void l2cap_streaming_send(struct sock *sk) +void l2cap_streaming_send(struct l2cap_chan *chan) { struct sk_buff *skb; - struct l2cap_pinfo *pi = l2cap_pi(sk); u16 control, fcs; - while ((skb = skb_dequeue(TX_QUEUE(sk)))) { + while ((skb = skb_dequeue(&chan->tx_q))) { control = get_unaligned_le16(skb->data + L2CAP_HDR_SIZE); - control |= pi->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT; + control |= chan->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT; put_unaligned_le16(control, skb->data + L2CAP_HDR_SIZE); - if (pi->fcs == L2CAP_FCS_CRC16) { + if (chan->fcs == L2CAP_FCS_CRC16) { fcs = crc16(0, (u8 *)skb->data, skb->len - 2); put_unaligned_le16(fcs, skb->data + skb->len - 2); } - l2cap_do_send(sk, skb); + l2cap_do_send(chan, skb); - pi->next_tx_seq = (pi->next_tx_seq + 1) % 64; + chan->next_tx_seq = (chan->next_tx_seq + 1) % 64; } } -static void l2cap_retransmit_one_frame(struct sock *sk, u8 tx_seq) +static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq) { - struct l2cap_pinfo *pi = l2cap_pi(sk); struct sk_buff *skb, *tx_skb; u16 control, fcs; - skb = skb_peek(TX_QUEUE(sk)); + skb = skb_peek(&chan->tx_q); if (!skb) return; @@ -1036,54 +1126,55 @@ static void l2cap_retransmit_one_frame(struct sock *sk, u8 tx_seq) if (bt_cb(skb)->tx_seq == tx_seq) break; - if (skb_queue_is_last(TX_QUEUE(sk), skb)) + if (skb_queue_is_last(&chan->tx_q, skb)) return; - } while ((skb = skb_queue_next(TX_QUEUE(sk), skb))); + } while ((skb = skb_queue_next(&chan->tx_q, skb))); - if (pi->remote_max_tx && - bt_cb(skb)->retries == pi->remote_max_tx) { - l2cap_send_disconn_req(pi->conn, sk, ECONNABORTED); + if (chan->remote_max_tx && + bt_cb(skb)->retries == chan->remote_max_tx) { + l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED); return; } tx_skb = skb_clone(skb, GFP_ATOMIC); bt_cb(skb)->retries++; control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE); + control &= L2CAP_CTRL_SAR; - if (pi->conn_state & L2CAP_CONN_SEND_FBIT) { + if (chan->conn_state & L2CAP_CONN_SEND_FBIT) { control |= L2CAP_CTRL_FINAL; - pi->conn_state &= ~L2CAP_CONN_SEND_FBIT; + chan->conn_state &= ~L2CAP_CONN_SEND_FBIT; } - control |= (pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT) + control |= (chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT) | (tx_seq << L2CAP_CTRL_TXSEQ_SHIFT); put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); - if (pi->fcs == L2CAP_FCS_CRC16) { + if (chan->fcs == L2CAP_FCS_CRC16) { fcs = crc16(0, (u8 *)tx_skb->data, tx_skb->len - 2); put_unaligned_le16(fcs, tx_skb->data + tx_skb->len - 2); } - l2cap_do_send(sk, tx_skb); + l2cap_do_send(chan, tx_skb); } -int l2cap_ertm_send(struct sock *sk) +int l2cap_ertm_send(struct l2cap_chan *chan) { struct sk_buff *skb, *tx_skb; - struct l2cap_pinfo *pi = l2cap_pi(sk); + struct sock *sk = chan->sk; u16 control, fcs; int nsent = 0; if (sk->sk_state != BT_CONNECTED) return -ENOTCONN; - while ((skb = sk->sk_send_head) && (!l2cap_tx_window_full(sk))) { + while ((skb = chan->tx_send_head) && (!l2cap_tx_window_full(chan))) { - if (pi->remote_max_tx && - bt_cb(skb)->retries == pi->remote_max_tx) { - l2cap_send_disconn_req(pi->conn, sk, ECONNABORTED); + if (chan->remote_max_tx && + bt_cb(skb)->retries == chan->remote_max_tx) { + l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED); break; } @@ -1094,36 +1185,36 @@ int l2cap_ertm_send(struct sock *sk) control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE); control &= L2CAP_CTRL_SAR; - if (pi->conn_state & L2CAP_CONN_SEND_FBIT) { + if (chan->conn_state & L2CAP_CONN_SEND_FBIT) { control |= L2CAP_CTRL_FINAL; - pi->conn_state &= ~L2CAP_CONN_SEND_FBIT; + chan->conn_state &= ~L2CAP_CONN_SEND_FBIT; } - control |= (pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT) - | (pi->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT); + control |= (chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT) + | (chan->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT); put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); - if (pi->fcs == L2CAP_FCS_CRC16) { + if (chan->fcs == L2CAP_FCS_CRC16) { fcs = crc16(0, (u8 *)skb->data, tx_skb->len - 2); put_unaligned_le16(fcs, skb->data + tx_skb->len - 2); } - l2cap_do_send(sk, tx_skb); + l2cap_do_send(chan, tx_skb); __mod_retrans_timer(); - bt_cb(skb)->tx_seq = pi->next_tx_seq; - pi->next_tx_seq = (pi->next_tx_seq + 1) % 64; + bt_cb(skb)->tx_seq = chan->next_tx_seq; + chan->next_tx_seq = (chan->next_tx_seq + 1) % 64; if (bt_cb(skb)->retries == 1) - pi->unacked_frames++; + chan->unacked_frames++; - pi->frames_sent++; + chan->frames_sent++; - if (skb_queue_is_last(TX_QUEUE(sk), skb)) - sk->sk_send_head = NULL; + if (skb_queue_is_last(&chan->tx_q, skb)) + chan->tx_send_head = NULL; else - sk->sk_send_head = skb_queue_next(TX_QUEUE(sk), skb); + chan->tx_send_head = skb_queue_next(&chan->tx_q, skb); nsent++; } @@ -1131,41 +1222,39 @@ int l2cap_ertm_send(struct sock *sk) return nsent; } -static int l2cap_retransmit_frames(struct sock *sk) +static int l2cap_retransmit_frames(struct l2cap_chan *chan) { - struct l2cap_pinfo *pi = l2cap_pi(sk); int ret; - if (!skb_queue_empty(TX_QUEUE(sk))) - sk->sk_send_head = TX_QUEUE(sk)->next; + if (!skb_queue_empty(&chan->tx_q)) + chan->tx_send_head = chan->tx_q.next; - pi->next_tx_seq = pi->expected_ack_seq; - ret = l2cap_ertm_send(sk); + chan->next_tx_seq = chan->expected_ack_seq; + ret = l2cap_ertm_send(chan); return ret; } -static void l2cap_send_ack(struct l2cap_pinfo *pi) +static void l2cap_send_ack(struct l2cap_chan *chan) { - struct sock *sk = (struct sock *)pi; u16 control = 0; - control |= pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; + control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; - if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY) { + if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) { control |= L2CAP_SUPER_RCV_NOT_READY; - pi->conn_state |= L2CAP_CONN_RNR_SENT; - l2cap_send_sframe(pi, control); + chan->conn_state |= L2CAP_CONN_RNR_SENT; + l2cap_send_sframe(chan, control); return; } - if (l2cap_ertm_send(sk) > 0) + if (l2cap_ertm_send(chan) > 0) return; control |= L2CAP_SUPER_RCV_READY; - l2cap_send_sframe(pi, control); + l2cap_send_sframe(chan, control); } -static void l2cap_send_srejtail(struct sock *sk) +static void l2cap_send_srejtail(struct l2cap_chan *chan) { struct srej_list *tail; u16 control; @@ -1173,15 +1262,15 @@ static void l2cap_send_srejtail(struct sock *sk) control = L2CAP_SUPER_SELECT_REJECT; control |= L2CAP_CTRL_FINAL; - tail = list_entry(SREJ_LIST(sk)->prev, struct srej_list, list); + tail = list_entry((&chan->srej_l)->prev, struct srej_list, list); control |= tail->tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; - l2cap_send_sframe(l2cap_pi(sk), control); + l2cap_send_sframe(chan, control); } static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, int len, int count, struct sk_buff *skb) { - struct l2cap_conn *conn = l2cap_pi(sk)->conn; + struct l2cap_conn *conn = l2cap_pi(sk)->chan->conn; struct sk_buff **frag; int err, sent = 0; @@ -1211,9 +1300,10 @@ static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, in return sent; } -struct sk_buff *l2cap_create_connless_pdu(struct sock *sk, struct msghdr *msg, size_t len) +struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len) { - struct l2cap_conn *conn = l2cap_pi(sk)->conn; + struct sock *sk = chan->sk; + struct l2cap_conn *conn = chan->conn; struct sk_buff *skb; int err, count, hlen = L2CAP_HDR_SIZE + 2; struct l2cap_hdr *lh; @@ -1228,9 +1318,9 @@ struct sk_buff *l2cap_create_connless_pdu(struct sock *sk, struct msghdr *msg, s /* Create L2CAP header */ lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); - lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid); + lh->cid = cpu_to_le16(chan->dcid); lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); - put_unaligned_le16(l2cap_pi(sk)->psm, skb_put(skb, 2)); + put_unaligned_le16(chan->psm, skb_put(skb, 2)); err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb); if (unlikely(err < 0)) { @@ -1240,9 +1330,10 @@ struct sk_buff *l2cap_create_connless_pdu(struct sock *sk, struct msghdr *msg, s return skb; } -struct sk_buff *l2cap_create_basic_pdu(struct sock *sk, struct msghdr *msg, size_t len) +struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len) { - struct l2cap_conn *conn = l2cap_pi(sk)->conn; + struct sock *sk = chan->sk; + struct l2cap_conn *conn = chan->conn; struct sk_buff *skb; int err, count, hlen = L2CAP_HDR_SIZE; struct l2cap_hdr *lh; @@ -1257,7 +1348,7 @@ struct sk_buff *l2cap_create_basic_pdu(struct sock *sk, struct msghdr *msg, size /* Create L2CAP header */ lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); - lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid); + lh->cid = cpu_to_le16(chan->dcid); lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb); @@ -1268,9 +1359,10 @@ struct sk_buff *l2cap_create_basic_pdu(struct sock *sk, struct msghdr *msg, size return skb; } -struct sk_buff *l2cap_create_iframe_pdu(struct sock *sk, struct msghdr *msg, size_t len, u16 control, u16 sdulen) +struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len, u16 control, u16 sdulen) { - struct l2cap_conn *conn = l2cap_pi(sk)->conn; + struct sock *sk = chan->sk; + struct l2cap_conn *conn = chan->conn; struct sk_buff *skb; int err, count, hlen = L2CAP_HDR_SIZE + 2; struct l2cap_hdr *lh; @@ -1283,7 +1375,7 @@ struct sk_buff *l2cap_create_iframe_pdu(struct sock *sk, struct msghdr *msg, siz if (sdulen) hlen += 2; - if (l2cap_pi(sk)->fcs == L2CAP_FCS_CRC16) + if (chan->fcs == L2CAP_FCS_CRC16) hlen += 2; count = min_t(unsigned int, (conn->mtu - hlen), len); @@ -1294,7 +1386,7 @@ struct sk_buff *l2cap_create_iframe_pdu(struct sock *sk, struct msghdr *msg, siz /* Create L2CAP header */ lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); - lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid); + lh->cid = cpu_to_le16(chan->dcid); lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); put_unaligned_le16(control, skb_put(skb, 2)); if (sdulen) @@ -1306,16 +1398,15 @@ struct sk_buff *l2cap_create_iframe_pdu(struct sock *sk, struct msghdr *msg, siz return ERR_PTR(err); } - if (l2cap_pi(sk)->fcs == L2CAP_FCS_CRC16) + if (chan->fcs == L2CAP_FCS_CRC16) put_unaligned_le16(0, skb_put(skb, 2)); bt_cb(skb)->retries = 0; return skb; } -int l2cap_sar_segment_sdu(struct sock *sk, struct msghdr *msg, size_t len) +int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len) { - struct l2cap_pinfo *pi = l2cap_pi(sk); struct sk_buff *skb; struct sk_buff_head sar_queue; u16 control; @@ -1323,26 +1414,26 @@ int l2cap_sar_segment_sdu(struct sock *sk, struct msghdr *msg, size_t len) skb_queue_head_init(&sar_queue); control = L2CAP_SDU_START; - skb = l2cap_create_iframe_pdu(sk, msg, pi->remote_mps, control, len); + skb = l2cap_create_iframe_pdu(chan, msg, chan->remote_mps, control, len); if (IS_ERR(skb)) return PTR_ERR(skb); __skb_queue_tail(&sar_queue, skb); - len -= pi->remote_mps; - size += pi->remote_mps; + len -= chan->remote_mps; + size += chan->remote_mps; while (len > 0) { size_t buflen; - if (len > pi->remote_mps) { + if (len > chan->remote_mps) { control = L2CAP_SDU_CONTINUE; - buflen = pi->remote_mps; + buflen = chan->remote_mps; } else { control = L2CAP_SDU_END; buflen = len; } - skb = l2cap_create_iframe_pdu(sk, msg, buflen, control, 0); + skb = l2cap_create_iframe_pdu(chan, msg, buflen, control, 0); if (IS_ERR(skb)) { skb_queue_purge(&sar_queue); return PTR_ERR(skb); @@ -1352,9 +1443,9 @@ int l2cap_sar_segment_sdu(struct sock *sk, struct msghdr *msg, size_t len) len -= buflen; size += buflen; } - skb_queue_splice_tail(&sar_queue, TX_QUEUE(sk)); - if (sk->sk_send_head == NULL) - sk->sk_send_head = sar_queue.next; + skb_queue_splice_tail(&sar_queue, &chan->tx_q); + if (chan->tx_send_head == NULL) + chan->tx_send_head = sar_queue.next; return size; } @@ -1362,10 +1453,11 @@ int l2cap_sar_segment_sdu(struct sock *sk, struct msghdr *msg, size_t len) static void l2cap_chan_ready(struct sock *sk) { struct sock *parent = bt_sk(sk)->parent; + struct l2cap_chan *chan = l2cap_pi(sk)->chan; BT_DBG("sk %p, parent %p", sk, parent); - l2cap_pi(sk)->conf_state = 0; + chan->conf_state = 0; l2cap_sock_clear_timer(sk); if (!parent) { @@ -1385,14 +1477,14 @@ static void l2cap_chan_ready(struct sock *sk) /* Copy frame to all raw sockets on that connection */ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb) { - struct l2cap_chan_list *l = &conn->chan_list; struct sk_buff *nskb; - struct sock *sk; + struct l2cap_chan *chan; BT_DBG("conn %p", conn); - read_lock(&l->lock); - for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { + read_lock(&conn->chan_lock); + list_for_each_entry(chan, &conn->chan_l, list) { + struct sock *sk = chan->sk; if (sk->sk_type != SOCK_RAW) continue; @@ -1406,7 +1498,7 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb) if (sock_queue_rcv_skb(sk, nskb)) kfree_skb(nskb); } - read_unlock(&l->lock); + read_unlock(&conn->chan_lock); } /* ---- L2CAP signalling commands ---- */ @@ -1538,32 +1630,35 @@ static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val) static void l2cap_ack_timeout(unsigned long arg) { - struct sock *sk = (void *) arg; + struct l2cap_chan *chan = (void *) arg; - bh_lock_sock(sk); - l2cap_send_ack(l2cap_pi(sk)); - bh_unlock_sock(sk); + bh_lock_sock(chan->sk); + l2cap_send_ack(chan); + bh_unlock_sock(chan->sk); } -static inline void l2cap_ertm_init(struct sock *sk) +static inline void l2cap_ertm_init(struct l2cap_chan *chan) { - l2cap_pi(sk)->expected_ack_seq = 0; - l2cap_pi(sk)->unacked_frames = 0; - l2cap_pi(sk)->buffer_seq = 0; - l2cap_pi(sk)->num_acked = 0; - l2cap_pi(sk)->frames_sent = 0; + struct sock *sk = chan->sk; + + chan->expected_ack_seq = 0; + chan->unacked_frames = 0; + chan->buffer_seq = 0; + chan->num_acked = 0; + chan->frames_sent = 0; + + setup_timer(&chan->retrans_timer, l2cap_retrans_timeout, + (unsigned long) chan); + setup_timer(&chan->monitor_timer, l2cap_monitor_timeout, + (unsigned long) chan); + setup_timer(&chan->ack_timer, l2cap_ack_timeout, (unsigned long) chan); - setup_timer(&l2cap_pi(sk)->retrans_timer, - l2cap_retrans_timeout, (unsigned long) sk); - setup_timer(&l2cap_pi(sk)->monitor_timer, - l2cap_monitor_timeout, (unsigned long) sk); - setup_timer(&l2cap_pi(sk)->ack_timer, - l2cap_ack_timeout, (unsigned long) sk); + skb_queue_head_init(&chan->srej_q); + skb_queue_head_init(&chan->busy_q); - __skb_queue_head_init(SREJ_QUEUE(sk)); - __skb_queue_head_init(BUSY_QUEUE(sk)); + INIT_LIST_HEAD(&chan->srej_l); - INIT_WORK(&l2cap_pi(sk)->busy_work, l2cap_busy_work); + INIT_WORK(&chan->busy_work, l2cap_busy_work); sk->sk_backlog_rcv = l2cap_ertm_data_rcv; } @@ -1581,38 +1676,37 @@ static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask) } } -int l2cap_build_conf_req(struct sock *sk, void *data) +static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data) { - struct l2cap_pinfo *pi = l2cap_pi(sk); struct l2cap_conf_req *req = data; - struct l2cap_conf_rfc rfc = { .mode = pi->mode }; + struct l2cap_conf_rfc rfc = { .mode = chan->mode }; void *ptr = req->data; - BT_DBG("sk %p", sk); + BT_DBG("chan %p", chan); - if (pi->num_conf_req || pi->num_conf_rsp) + if (chan->num_conf_req || chan->num_conf_rsp) goto done; - switch (pi->mode) { + switch (chan->mode) { case L2CAP_MODE_STREAMING: case L2CAP_MODE_ERTM: - if (pi->conf_state & L2CAP_CONF_STATE2_DEVICE) + if (chan->conf_state & L2CAP_CONF_STATE2_DEVICE) break; /* fall through */ default: - pi->mode = l2cap_select_mode(rfc.mode, pi->conn->feat_mask); + chan->mode = l2cap_select_mode(rfc.mode, chan->conn->feat_mask); break; } done: - if (pi->imtu != L2CAP_DEFAULT_MTU) - l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu); + if (chan->imtu != L2CAP_DEFAULT_MTU) + l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu); - switch (pi->mode) { + switch (chan->mode) { case L2CAP_MODE_BASIC: - if (!(pi->conn->feat_mask & L2CAP_FEAT_ERTM) && - !(pi->conn->feat_mask & L2CAP_FEAT_STREAMING)) + if (!(chan->conn->feat_mask & L2CAP_FEAT_ERTM) && + !(chan->conn->feat_mask & L2CAP_FEAT_STREAMING)) break; rfc.mode = L2CAP_MODE_BASIC; @@ -1628,24 +1722,24 @@ done: case L2CAP_MODE_ERTM: rfc.mode = L2CAP_MODE_ERTM; - rfc.txwin_size = pi->tx_win; - rfc.max_transmit = pi->max_tx; + rfc.txwin_size = chan->tx_win; + rfc.max_transmit = chan->max_tx; rfc.retrans_timeout = 0; rfc.monitor_timeout = 0; rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE); - if (L2CAP_DEFAULT_MAX_PDU_SIZE > pi->conn->mtu - 10) - rfc.max_pdu_size = cpu_to_le16(pi->conn->mtu - 10); + if (L2CAP_DEFAULT_MAX_PDU_SIZE > chan->conn->mtu - 10) + rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10); l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); - if (!(pi->conn->feat_mask & L2CAP_FEAT_FCS)) + if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS)) break; - if (pi->fcs == L2CAP_FCS_NONE || - pi->conf_state & L2CAP_CONF_NO_FCS_RECV) { - pi->fcs = L2CAP_FCS_NONE; - l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, pi->fcs); + if (chan->fcs == L2CAP_FCS_NONE || + chan->conf_state & L2CAP_CONF_NO_FCS_RECV) { + chan->fcs = L2CAP_FCS_NONE; + l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs); } break; @@ -1656,43 +1750,42 @@ done: rfc.retrans_timeout = 0; rfc.monitor_timeout = 0; rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE); - if (L2CAP_DEFAULT_MAX_PDU_SIZE > pi->conn->mtu - 10) - rfc.max_pdu_size = cpu_to_le16(pi->conn->mtu - 10); + if (L2CAP_DEFAULT_MAX_PDU_SIZE > chan->conn->mtu - 10) + rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10); l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); - if (!(pi->conn->feat_mask & L2CAP_FEAT_FCS)) + if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS)) break; - if (pi->fcs == L2CAP_FCS_NONE || - pi->conf_state & L2CAP_CONF_NO_FCS_RECV) { - pi->fcs = L2CAP_FCS_NONE; - l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, pi->fcs); + if (chan->fcs == L2CAP_FCS_NONE || + chan->conf_state & L2CAP_CONF_NO_FCS_RECV) { + chan->fcs = L2CAP_FCS_NONE; + l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs); } break; } - req->dcid = cpu_to_le16(pi->dcid); + req->dcid = cpu_to_le16(chan->dcid); req->flags = cpu_to_le16(0); return ptr - data; } -static int l2cap_parse_conf_req(struct sock *sk, void *data) +static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data) { - struct l2cap_pinfo *pi = l2cap_pi(sk); struct l2cap_conf_rsp *rsp = data; void *ptr = rsp->data; - void *req = pi->conf_req; - int len = pi->conf_len; + void *req = chan->conf_req; + int len = chan->conf_len; int type, hint, olen; unsigned long val; struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC }; u16 mtu = L2CAP_DEFAULT_MTU; u16 result = L2CAP_CONF_SUCCESS; - BT_DBG("sk %p", sk); + BT_DBG("chan %p", chan); while (len >= L2CAP_CONF_OPT_SIZE) { len -= l2cap_get_conf_opt(&req, &type, &olen, &val); @@ -1706,7 +1799,7 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data) break; case L2CAP_CONF_FLUSH_TO: - pi->flush_to = val; + chan->flush_to = val; break; case L2CAP_CONF_QOS: @@ -1719,7 +1812,7 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data) case L2CAP_CONF_FCS: if (val == L2CAP_FCS_NONE) - pi->conf_state |= L2CAP_CONF_NO_FCS_RECV; + chan->conf_state |= L2CAP_CONF_NO_FCS_RECV; break; @@ -1733,30 +1826,30 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data) } } - if (pi->num_conf_rsp || pi->num_conf_req > 1) + if (chan->num_conf_rsp || chan->num_conf_req > 1) goto done; - switch (pi->mode) { + switch (chan->mode) { case L2CAP_MODE_STREAMING: case L2CAP_MODE_ERTM: - if (!(pi->conf_state & L2CAP_CONF_STATE2_DEVICE)) { - pi->mode = l2cap_select_mode(rfc.mode, - pi->conn->feat_mask); + if (!(chan->conf_state & L2CAP_CONF_STATE2_DEVICE)) { + chan->mode = l2cap_select_mode(rfc.mode, + chan->conn->feat_mask); break; } - if (pi->mode != rfc.mode) + if (chan->mode != rfc.mode) return -ECONNREFUSED; break; } done: - if (pi->mode != rfc.mode) { + if (chan->mode != rfc.mode) { result = L2CAP_CONF_UNACCEPT; - rfc.mode = pi->mode; + rfc.mode = chan->mode; - if (pi->num_conf_rsp == 1) + if (chan->num_conf_rsp == 1) return -ECONNREFUSED; l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, @@ -1771,32 +1864,32 @@ done: if (mtu < L2CAP_DEFAULT_MIN_MTU) result = L2CAP_CONF_UNACCEPT; else { - pi->omtu = mtu; - pi->conf_state |= L2CAP_CONF_MTU_DONE; + chan->omtu = mtu; + chan->conf_state |= L2CAP_CONF_MTU_DONE; } - l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu); + l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->omtu); switch (rfc.mode) { case L2CAP_MODE_BASIC: - pi->fcs = L2CAP_FCS_NONE; - pi->conf_state |= L2CAP_CONF_MODE_DONE; + chan->fcs = L2CAP_FCS_NONE; + chan->conf_state |= L2CAP_CONF_MODE_DONE; break; case L2CAP_MODE_ERTM: - pi->remote_tx_win = rfc.txwin_size; - pi->remote_max_tx = rfc.max_transmit; + chan->remote_tx_win = rfc.txwin_size; + chan->remote_max_tx = rfc.max_transmit; - if (le16_to_cpu(rfc.max_pdu_size) > pi->conn->mtu - 10) - rfc.max_pdu_size = cpu_to_le16(pi->conn->mtu - 10); + if (le16_to_cpu(rfc.max_pdu_size) > chan->conn->mtu - 10) + rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10); - pi->remote_mps = le16_to_cpu(rfc.max_pdu_size); + chan->remote_mps = le16_to_cpu(rfc.max_pdu_size); rfc.retrans_timeout = le16_to_cpu(L2CAP_DEFAULT_RETRANS_TO); rfc.monitor_timeout = le16_to_cpu(L2CAP_DEFAULT_MONITOR_TO); - pi->conf_state |= L2CAP_CONF_MODE_DONE; + chan->conf_state |= L2CAP_CONF_MODE_DONE; l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); @@ -1804,12 +1897,12 @@ done: break; case L2CAP_MODE_STREAMING: - if (le16_to_cpu(rfc.max_pdu_size) > pi->conn->mtu - 10) - rfc.max_pdu_size = cpu_to_le16(pi->conn->mtu - 10); + if (le16_to_cpu(rfc.max_pdu_size) > chan->conn->mtu - 10) + rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10); - pi->remote_mps = le16_to_cpu(rfc.max_pdu_size); + chan->remote_mps = le16_to_cpu(rfc.max_pdu_size); - pi->conf_state |= L2CAP_CONF_MODE_DONE; + chan->conf_state |= L2CAP_CONF_MODE_DONE; l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); @@ -1820,29 +1913,28 @@ done: result = L2CAP_CONF_UNACCEPT; memset(&rfc, 0, sizeof(rfc)); - rfc.mode = pi->mode; + rfc.mode = chan->mode; } if (result == L2CAP_CONF_SUCCESS) - pi->conf_state |= L2CAP_CONF_OUTPUT_DONE; + chan->conf_state |= L2CAP_CONF_OUTPUT_DONE; } - rsp->scid = cpu_to_le16(pi->dcid); + rsp->scid = cpu_to_le16(chan->dcid); rsp->result = cpu_to_le16(result); rsp->flags = cpu_to_le16(0x0000); return ptr - data; } -static int l2cap_parse_conf_rsp(struct sock *sk, void *rsp, int len, void *data, u16 *result) +static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, void *data, u16 *result) { - struct l2cap_pinfo *pi = l2cap_pi(sk); struct l2cap_conf_req *req = data; void *ptr = req->data; int type, olen; unsigned long val; struct l2cap_conf_rfc rfc; - BT_DBG("sk %p, rsp %p, len %d, req %p", sk, rsp, len, data); + BT_DBG("chan %p, rsp %p, len %d, req %p", chan, rsp, len, data); while (len >= L2CAP_CONF_OPT_SIZE) { len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val); @@ -1851,27 +1943,27 @@ static int l2cap_parse_conf_rsp(struct sock *sk, void *rsp, int len, void *data, case L2CAP_CONF_MTU: if (val < L2CAP_DEFAULT_MIN_MTU) { *result = L2CAP_CONF_UNACCEPT; - pi->imtu = L2CAP_DEFAULT_MIN_MTU; + chan->imtu = L2CAP_DEFAULT_MIN_MTU; } else - pi->imtu = val; - l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu); + chan->imtu = val; + l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu); break; case L2CAP_CONF_FLUSH_TO: - pi->flush_to = val; + chan->flush_to = val; l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, - 2, pi->flush_to); + 2, chan->flush_to); break; case L2CAP_CONF_RFC: if (olen == sizeof(rfc)) memcpy(&rfc, (void *)val, olen); - if ((pi->conf_state & L2CAP_CONF_STATE2_DEVICE) && - rfc.mode != pi->mode) + if ((chan->conf_state & L2CAP_CONF_STATE2_DEVICE) && + rfc.mode != chan->mode) return -ECONNREFUSED; - pi->fcs = 0; + chan->fcs = 0; l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); @@ -1879,53 +1971,74 @@ static int l2cap_parse_conf_rsp(struct sock *sk, void *rsp, int len, void *data, } } - if (pi->mode == L2CAP_MODE_BASIC && pi->mode != rfc.mode) + if (chan->mode == L2CAP_MODE_BASIC && chan->mode != rfc.mode) return -ECONNREFUSED; - pi->mode = rfc.mode; + chan->mode = rfc.mode; if (*result == L2CAP_CONF_SUCCESS) { switch (rfc.mode) { case L2CAP_MODE_ERTM: - pi->retrans_timeout = le16_to_cpu(rfc.retrans_timeout); - pi->monitor_timeout = le16_to_cpu(rfc.monitor_timeout); - pi->mps = le16_to_cpu(rfc.max_pdu_size); + chan->retrans_timeout = le16_to_cpu(rfc.retrans_timeout); + chan->monitor_timeout = le16_to_cpu(rfc.monitor_timeout); + chan->mps = le16_to_cpu(rfc.max_pdu_size); break; case L2CAP_MODE_STREAMING: - pi->mps = le16_to_cpu(rfc.max_pdu_size); + chan->mps = le16_to_cpu(rfc.max_pdu_size); } } - req->dcid = cpu_to_le16(pi->dcid); + req->dcid = cpu_to_le16(chan->dcid); req->flags = cpu_to_le16(0x0000); return ptr - data; } -static int l2cap_build_conf_rsp(struct sock *sk, void *data, u16 result, u16 flags) +static int l2cap_build_conf_rsp(struct l2cap_chan *chan, void *data, u16 result, u16 flags) { struct l2cap_conf_rsp *rsp = data; void *ptr = rsp->data; - BT_DBG("sk %p", sk); + BT_DBG("chan %p", chan); - rsp->scid = cpu_to_le16(l2cap_pi(sk)->dcid); + rsp->scid = cpu_to_le16(chan->dcid); rsp->result = cpu_to_le16(result); rsp->flags = cpu_to_le16(flags); return ptr - data; } -static void l2cap_conf_rfc_get(struct sock *sk, void *rsp, int len) +void __l2cap_connect_rsp_defer(struct l2cap_chan *chan) +{ + struct l2cap_conn_rsp rsp; + struct l2cap_conn *conn = chan->conn; + u8 buf[128]; + + rsp.scid = cpu_to_le16(chan->dcid); + rsp.dcid = cpu_to_le16(chan->scid); + rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS); + rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); + l2cap_send_cmd(conn, chan->ident, + L2CAP_CONN_RSP, sizeof(rsp), &rsp); + + if (chan->conf_state & L2CAP_CONF_REQ_SENT) + return; + + chan->conf_state |= L2CAP_CONF_REQ_SENT; + l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, + l2cap_build_conf_req(chan, buf), buf); + chan->num_conf_req++; +} + +static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len) { - struct l2cap_pinfo *pi = l2cap_pi(sk); int type, olen; unsigned long val; struct l2cap_conf_rfc rfc; - BT_DBG("sk %p, rsp %p, len %d", sk, rsp, len); + BT_DBG("chan %p, rsp %p, len %d", chan, rsp, len); - if ((pi->mode != L2CAP_MODE_ERTM) && (pi->mode != L2CAP_MODE_STREAMING)) + if ((chan->mode != L2CAP_MODE_ERTM) && (chan->mode != L2CAP_MODE_STREAMING)) return; while (len >= L2CAP_CONF_OPT_SIZE) { @@ -1942,12 +2055,12 @@ static void l2cap_conf_rfc_get(struct sock *sk, void *rsp, int len) done: switch (rfc.mode) { case L2CAP_MODE_ERTM: - pi->retrans_timeout = le16_to_cpu(rfc.retrans_timeout); - pi->monitor_timeout = le16_to_cpu(rfc.monitor_timeout); - pi->mps = le16_to_cpu(rfc.max_pdu_size); + chan->retrans_timeout = le16_to_cpu(rfc.retrans_timeout); + chan->monitor_timeout = le16_to_cpu(rfc.monitor_timeout); + chan->mps = le16_to_cpu(rfc.max_pdu_size); break; case L2CAP_MODE_STREAMING: - pi->mps = le16_to_cpu(rfc.max_pdu_size); + chan->mps = le16_to_cpu(rfc.max_pdu_size); } } @@ -1973,9 +2086,9 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hd static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) { - struct l2cap_chan_list *list = &conn->chan_list; struct l2cap_conn_req *req = (struct l2cap_conn_req *) data; struct l2cap_conn_rsp rsp; + struct l2cap_chan *chan = NULL, *pchan; struct sock *parent, *sk = NULL; int result, status = L2CAP_CS_NO_INFO; @@ -1985,12 +2098,14 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd BT_DBG("psm 0x%2.2x scid 0x%4.4x", psm, scid); /* Check if we have socket listening on psm */ - parent = l2cap_get_sock_by_psm(BT_LISTEN, psm, conn->src); - if (!parent) { + pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, conn->src); + if (!pchan) { result = L2CAP_CR_BAD_PSM; goto sendresp; } + parent = pchan->sk; + bh_lock_sock(parent); /* Check if the ACL is secure enough (if not SDP) */ @@ -2013,11 +2128,19 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd if (!sk) goto response; - write_lock_bh(&list->lock); + chan = l2cap_chan_create(sk); + if (!chan) { + l2cap_sock_kill(sk); + goto response; + } + + l2cap_pi(sk)->chan = chan; + + write_lock_bh(&conn->chan_lock); /* Check if we already have channel with that dcid */ - if (__l2cap_get_chan_by_dcid(list, scid)) { - write_unlock_bh(&list->lock); + if (__l2cap_get_chan_by_dcid(conn, scid)) { + write_unlock_bh(&conn->chan_lock); sock_set_flag(sk, SOCK_ZAPPED); l2cap_sock_kill(sk); goto response; @@ -2028,20 +2151,21 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd l2cap_sock_init(sk, parent); bacpy(&bt_sk(sk)->src, conn->src); bacpy(&bt_sk(sk)->dst, conn->dst); - l2cap_pi(sk)->psm = psm; - l2cap_pi(sk)->dcid = scid; + chan->psm = psm; + chan->dcid = scid; bt_accept_enqueue(parent, sk); - __l2cap_chan_add(conn, sk); - dcid = l2cap_pi(sk)->scid; + __l2cap_chan_add(conn, chan); + + dcid = chan->scid; l2cap_sock_set_timer(sk, sk->sk_sndtimeo); - l2cap_pi(sk)->ident = cmd->ident; + chan->ident = cmd->ident; if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) { - if (l2cap_check_security(sk)) { + if (l2cap_check_security(chan)) { if (bt_sk(sk)->defer_setup) { sk->sk_state = BT_CONNECT2; result = L2CAP_CR_PEND; @@ -2063,7 +2187,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd status = L2CAP_CS_NO_INFO; } - write_unlock_bh(&list->lock); + write_unlock_bh(&conn->chan_lock); response: bh_unlock_sock(parent); @@ -2089,13 +2213,13 @@ sendresp: L2CAP_INFO_REQ, sizeof(info), &info); } - if (sk && !(l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT) && + if (chan && !(chan->conf_state & L2CAP_CONF_REQ_SENT) && result == L2CAP_CR_SUCCESS) { u8 buf[128]; - l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT; + chan->conf_state |= L2CAP_CONF_REQ_SENT; l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, - l2cap_build_conf_req(sk, buf), buf); - l2cap_pi(sk)->num_conf_req++; + l2cap_build_conf_req(chan, buf), buf); + chan->num_conf_req++; } return 0; @@ -2105,6 +2229,7 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd { struct l2cap_conn_rsp *rsp = (struct l2cap_conn_rsp *) data; u16 scid, dcid, result, status; + struct l2cap_chan *chan; struct sock *sk; u8 req[128]; @@ -2116,34 +2241,36 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x", dcid, scid, result, status); if (scid) { - sk = l2cap_get_chan_by_scid(&conn->chan_list, scid); - if (!sk) + chan = l2cap_get_chan_by_scid(conn, scid); + if (!chan) return -EFAULT; } else { - sk = l2cap_get_chan_by_ident(&conn->chan_list, cmd->ident); - if (!sk) + chan = l2cap_get_chan_by_ident(conn, cmd->ident); + if (!chan) return -EFAULT; } + sk = chan->sk; + switch (result) { case L2CAP_CR_SUCCESS: sk->sk_state = BT_CONFIG; - l2cap_pi(sk)->ident = 0; - l2cap_pi(sk)->dcid = dcid; - l2cap_pi(sk)->conf_state &= ~L2CAP_CONF_CONNECT_PEND; + chan->ident = 0; + chan->dcid = dcid; + chan->conf_state &= ~L2CAP_CONF_CONNECT_PEND; - if (l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT) + if (chan->conf_state & L2CAP_CONF_REQ_SENT) break; - l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT; + chan->conf_state |= L2CAP_CONF_REQ_SENT; l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, - l2cap_build_conf_req(sk, req), req); - l2cap_pi(sk)->num_conf_req++; + l2cap_build_conf_req(chan, req), req); + chan->num_conf_req++; break; case L2CAP_CR_PEND: - l2cap_pi(sk)->conf_state |= L2CAP_CONF_CONNECT_PEND; + chan->conf_state |= L2CAP_CONF_CONNECT_PEND; break; default: @@ -2155,7 +2282,7 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd break; } - l2cap_chan_del(sk, ECONNREFUSED); + l2cap_chan_del(chan, ECONNREFUSED); break; } @@ -2163,15 +2290,17 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd return 0; } -static inline void set_default_fcs(struct l2cap_pinfo *pi) +static inline void set_default_fcs(struct l2cap_chan *chan) { + struct l2cap_pinfo *pi = l2cap_pi(chan->sk); + /* FCS is enabled only in ERTM or streaming mode, if one or both * sides request it. */ - if (pi->mode != L2CAP_MODE_ERTM && pi->mode != L2CAP_MODE_STREAMING) - pi->fcs = L2CAP_FCS_NONE; - else if (!(pi->conf_state & L2CAP_CONF_NO_FCS_RECV)) - pi->fcs = L2CAP_FCS_CRC16; + if (chan->mode != L2CAP_MODE_ERTM && chan->mode != L2CAP_MODE_STREAMING) + chan->fcs = L2CAP_FCS_NONE; + else if (!(pi->chan->conf_state & L2CAP_CONF_NO_FCS_RECV)) + chan->fcs = L2CAP_FCS_CRC16; } static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data) @@ -2179,6 +2308,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr struct l2cap_conf_req *req = (struct l2cap_conf_req *) data; u16 dcid, flags; u8 rsp[64]; + struct l2cap_chan *chan; struct sock *sk; int len; @@ -2187,10 +2317,12 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr BT_DBG("dcid 0x%4.4x flags 0x%2.2x", dcid, flags); - sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid); - if (!sk) + chan = l2cap_get_chan_by_scid(conn, dcid); + if (!chan) return -ENOENT; + sk = chan->sk; + if (sk->sk_state != BT_CONFIG) { struct l2cap_cmd_rej rej; @@ -2202,62 +2334,62 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr /* Reject if config buffer is too small. */ len = cmd_len - sizeof(*req); - if (l2cap_pi(sk)->conf_len + len > sizeof(l2cap_pi(sk)->conf_req)) { + if (chan->conf_len + len > sizeof(chan->conf_req)) { l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, - l2cap_build_conf_rsp(sk, rsp, + l2cap_build_conf_rsp(chan, rsp, L2CAP_CONF_REJECT, flags), rsp); goto unlock; } /* Store config. */ - memcpy(l2cap_pi(sk)->conf_req + l2cap_pi(sk)->conf_len, req->data, len); - l2cap_pi(sk)->conf_len += len; + memcpy(chan->conf_req + chan->conf_len, req->data, len); + chan->conf_len += len; if (flags & 0x0001) { /* Incomplete config. Send empty response. */ l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, - l2cap_build_conf_rsp(sk, rsp, + l2cap_build_conf_rsp(chan, rsp, L2CAP_CONF_SUCCESS, 0x0001), rsp); goto unlock; } /* Complete config. */ - len = l2cap_parse_conf_req(sk, rsp); + len = l2cap_parse_conf_req(chan, rsp); if (len < 0) { - l2cap_send_disconn_req(conn, sk, ECONNRESET); + l2cap_send_disconn_req(conn, chan, ECONNRESET); goto unlock; } l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, len, rsp); - l2cap_pi(sk)->num_conf_rsp++; + chan->num_conf_rsp++; /* Reset config buffer. */ - l2cap_pi(sk)->conf_len = 0; + chan->conf_len = 0; - if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_OUTPUT_DONE)) + if (!(chan->conf_state & L2CAP_CONF_OUTPUT_DONE)) goto unlock; - if (l2cap_pi(sk)->conf_state & L2CAP_CONF_INPUT_DONE) { - set_default_fcs(l2cap_pi(sk)); + if (chan->conf_state & L2CAP_CONF_INPUT_DONE) { + set_default_fcs(chan); sk->sk_state = BT_CONNECTED; - l2cap_pi(sk)->next_tx_seq = 0; - l2cap_pi(sk)->expected_tx_seq = 0; - __skb_queue_head_init(TX_QUEUE(sk)); - if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) - l2cap_ertm_init(sk); + chan->next_tx_seq = 0; + chan->expected_tx_seq = 0; + skb_queue_head_init(&chan->tx_q); + if (chan->mode == L2CAP_MODE_ERTM) + l2cap_ertm_init(chan); l2cap_chan_ready(sk); goto unlock; } - if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT)) { + if (!(chan->conf_state & L2CAP_CONF_REQ_SENT)) { u8 buf[64]; - l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT; + chan->conf_state |= L2CAP_CONF_REQ_SENT; l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, - l2cap_build_conf_req(sk, buf), buf); - l2cap_pi(sk)->num_conf_req++; + l2cap_build_conf_req(chan, buf), buf); + chan->num_conf_req++; } unlock: @@ -2269,6 +2401,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr { struct l2cap_conf_rsp *rsp = (struct l2cap_conf_rsp *)data; u16 scid, flags, result; + struct l2cap_chan *chan; struct sock *sk; int len = cmd->len - sizeof(*rsp); @@ -2279,36 +2412,38 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr BT_DBG("scid 0x%4.4x flags 0x%2.2x result 0x%2.2x", scid, flags, result); - sk = l2cap_get_chan_by_scid(&conn->chan_list, scid); - if (!sk) + chan = l2cap_get_chan_by_scid(conn, scid); + if (!chan) return 0; + sk = chan->sk; + switch (result) { case L2CAP_CONF_SUCCESS: - l2cap_conf_rfc_get(sk, rsp->data, len); + l2cap_conf_rfc_get(chan, rsp->data, len); break; case L2CAP_CONF_UNACCEPT: - if (l2cap_pi(sk)->num_conf_rsp <= L2CAP_CONF_MAX_CONF_RSP) { + if (chan->num_conf_rsp <= L2CAP_CONF_MAX_CONF_RSP) { char req[64]; if (len > sizeof(req) - sizeof(struct l2cap_conf_req)) { - l2cap_send_disconn_req(conn, sk, ECONNRESET); + l2cap_send_disconn_req(conn, chan, ECONNRESET); goto done; } /* throw out any old stored conf requests */ result = L2CAP_CONF_SUCCESS; - len = l2cap_parse_conf_rsp(sk, rsp->data, - len, req, &result); + len = l2cap_parse_conf_rsp(chan, rsp->data, len, + req, &result); if (len < 0) { - l2cap_send_disconn_req(conn, sk, ECONNRESET); + l2cap_send_disconn_req(conn, chan, ECONNRESET); goto done; } l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, len, req); - l2cap_pi(sk)->num_conf_req++; + chan->num_conf_req++; if (result != L2CAP_CONF_SUCCESS) goto done; break; @@ -2317,24 +2452,24 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr default: sk->sk_err = ECONNRESET; l2cap_sock_set_timer(sk, HZ * 5); - l2cap_send_disconn_req(conn, sk, ECONNRESET); + l2cap_send_disconn_req(conn, chan, ECONNRESET); goto done; } if (flags & 0x01) goto done; - l2cap_pi(sk)->conf_state |= L2CAP_CONF_INPUT_DONE; + chan->conf_state |= L2CAP_CONF_INPUT_DONE; - if (l2cap_pi(sk)->conf_state & L2CAP_CONF_OUTPUT_DONE) { - set_default_fcs(l2cap_pi(sk)); + if (chan->conf_state & L2CAP_CONF_OUTPUT_DONE) { + set_default_fcs(chan); sk->sk_state = BT_CONNECTED; - l2cap_pi(sk)->next_tx_seq = 0; - l2cap_pi(sk)->expected_tx_seq = 0; - __skb_queue_head_init(TX_QUEUE(sk)); - if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) - l2cap_ertm_init(sk); + chan->next_tx_seq = 0; + chan->expected_tx_seq = 0; + skb_queue_head_init(&chan->tx_q); + if (chan->mode == L2CAP_MODE_ERTM) + l2cap_ertm_init(chan); l2cap_chan_ready(sk); } @@ -2349,6 +2484,7 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd struct l2cap_disconn_req *req = (struct l2cap_disconn_req *) data; struct l2cap_disconn_rsp rsp; u16 dcid, scid; + struct l2cap_chan *chan; struct sock *sk; scid = __le16_to_cpu(req->scid); @@ -2356,12 +2492,14 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd BT_DBG("scid 0x%4.4x dcid 0x%4.4x", scid, dcid); - sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid); - if (!sk) + chan = l2cap_get_chan_by_scid(conn, dcid); + if (!chan) return 0; - rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); - rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); + sk = chan->sk; + + rsp.dcid = cpu_to_le16(chan->scid); + rsp.scid = cpu_to_le16(chan->dcid); l2cap_send_cmd(conn, cmd->ident, L2CAP_DISCONN_RSP, sizeof(rsp), &rsp); sk->sk_shutdown = SHUTDOWN_MASK; @@ -2375,7 +2513,7 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd return 0; } - l2cap_chan_del(sk, ECONNRESET); + l2cap_chan_del(chan, ECONNRESET); bh_unlock_sock(sk); l2cap_sock_kill(sk); @@ -2386,6 +2524,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd { struct l2cap_disconn_rsp *rsp = (struct l2cap_disconn_rsp *) data; u16 dcid, scid; + struct l2cap_chan *chan; struct sock *sk; scid = __le16_to_cpu(rsp->scid); @@ -2393,10 +2532,12 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd BT_DBG("dcid 0x%4.4x scid 0x%4.4x", dcid, scid); - sk = l2cap_get_chan_by_scid(&conn->chan_list, scid); - if (!sk) + chan = l2cap_get_chan_by_scid(conn, scid); + if (!chan) return 0; + sk = chan->sk; + /* don't delete l2cap channel if sk is owned by user */ if (sock_owned_by_user(sk)) { sk->sk_state = BT_DISCONN; @@ -2406,7 +2547,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd return 0; } - l2cap_chan_del(sk, 0); + l2cap_chan_del(chan, 0); bh_unlock_sock(sk); l2cap_sock_kill(sk); @@ -2693,12 +2834,12 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn, kfree_skb(skb); } -static int l2cap_check_fcs(struct l2cap_pinfo *pi, struct sk_buff *skb) +static int l2cap_check_fcs(struct l2cap_chan *chan, struct sk_buff *skb) { u16 our_fcs, rcv_fcs; int hdr_size = L2CAP_HDR_SIZE + 2; - if (pi->fcs == L2CAP_FCS_CRC16) { + if (chan->fcs == L2CAP_FCS_CRC16) { skb_trim(skb, skb->len - 2); rcv_fcs = get_unaligned_le16(skb->data + skb->len); our_fcs = crc16(0, skb->data - hdr_size, skb->len + hdr_size); @@ -2709,49 +2850,47 @@ static int l2cap_check_fcs(struct l2cap_pinfo *pi, struct sk_buff *skb) return 0; } -static inline void l2cap_send_i_or_rr_or_rnr(struct sock *sk) +static inline void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan) { - struct l2cap_pinfo *pi = l2cap_pi(sk); u16 control = 0; - pi->frames_sent = 0; + chan->frames_sent = 0; - control |= pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; + control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; - if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY) { + if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) { control |= L2CAP_SUPER_RCV_NOT_READY; - l2cap_send_sframe(pi, control); - pi->conn_state |= L2CAP_CONN_RNR_SENT; + l2cap_send_sframe(chan, control); + chan->conn_state |= L2CAP_CONN_RNR_SENT; } - if (pi->conn_state & L2CAP_CONN_REMOTE_BUSY) - l2cap_retransmit_frames(sk); + if (chan->conn_state & L2CAP_CONN_REMOTE_BUSY) + l2cap_retransmit_frames(chan); - l2cap_ertm_send(sk); + l2cap_ertm_send(chan); - if (!(pi->conn_state & L2CAP_CONN_LOCAL_BUSY) && - pi->frames_sent == 0) { + if (!(chan->conn_state & L2CAP_CONN_LOCAL_BUSY) && + chan->frames_sent == 0) { control |= L2CAP_SUPER_RCV_READY; - l2cap_send_sframe(pi, control); + l2cap_send_sframe(chan, control); } } -static int l2cap_add_to_srej_queue(struct sock *sk, struct sk_buff *skb, u8 tx_seq, u8 sar) +static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb, u8 tx_seq, u8 sar) { struct sk_buff *next_skb; - struct l2cap_pinfo *pi = l2cap_pi(sk); int tx_seq_offset, next_tx_seq_offset; bt_cb(skb)->tx_seq = tx_seq; bt_cb(skb)->sar = sar; - next_skb = skb_peek(SREJ_QUEUE(sk)); + next_skb = skb_peek(&chan->srej_q); if (!next_skb) { - __skb_queue_tail(SREJ_QUEUE(sk), skb); + __skb_queue_tail(&chan->srej_q, skb); return 0; } - tx_seq_offset = (tx_seq - pi->buffer_seq) % 64; + tx_seq_offset = (tx_seq - chan->buffer_seq) % 64; if (tx_seq_offset < 0) tx_seq_offset += 64; @@ -2760,53 +2899,52 @@ static int l2cap_add_to_srej_queue(struct sock *sk, struct sk_buff *skb, u8 tx_s return -EINVAL; next_tx_seq_offset = (bt_cb(next_skb)->tx_seq - - pi->buffer_seq) % 64; + chan->buffer_seq) % 64; if (next_tx_seq_offset < 0) next_tx_seq_offset += 64; if (next_tx_seq_offset > tx_seq_offset) { - __skb_queue_before(SREJ_QUEUE(sk), next_skb, skb); + __skb_queue_before(&chan->srej_q, next_skb, skb); return 0; } - if (skb_queue_is_last(SREJ_QUEUE(sk), next_skb)) + if (skb_queue_is_last(&chan->srej_q, next_skb)) break; - } while ((next_skb = skb_queue_next(SREJ_QUEUE(sk), next_skb))); + } while ((next_skb = skb_queue_next(&chan->srej_q, next_skb))); - __skb_queue_tail(SREJ_QUEUE(sk), skb); + __skb_queue_tail(&chan->srej_q, skb); return 0; } -static int l2cap_ertm_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 control) +static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u16 control) { - struct l2cap_pinfo *pi = l2cap_pi(sk); struct sk_buff *_skb; int err; switch (control & L2CAP_CTRL_SAR) { case L2CAP_SDU_UNSEGMENTED: - if (pi->conn_state & L2CAP_CONN_SAR_SDU) + if (chan->conn_state & L2CAP_CONN_SAR_SDU) goto drop; - err = sock_queue_rcv_skb(sk, skb); + err = sock_queue_rcv_skb(chan->sk, skb); if (!err) return err; break; case L2CAP_SDU_START: - if (pi->conn_state & L2CAP_CONN_SAR_SDU) + if (chan->conn_state & L2CAP_CONN_SAR_SDU) goto drop; - pi->sdu_len = get_unaligned_le16(skb->data); + chan->sdu_len = get_unaligned_le16(skb->data); - if (pi->sdu_len > pi->imtu) + if (chan->sdu_len > chan->imtu) goto disconnect; - pi->sdu = bt_skb_alloc(pi->sdu_len, GFP_ATOMIC); - if (!pi->sdu) + chan->sdu = bt_skb_alloc(chan->sdu_len, GFP_ATOMIC); + if (!chan->sdu) return -ENOMEM; /* pull sdu_len bytes only after alloc, because of Local Busy @@ -2814,63 +2952,63 @@ static int l2cap_ertm_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 c * only once, i.e., when alloc does not fail */ skb_pull(skb, 2); - memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len); + memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); - pi->conn_state |= L2CAP_CONN_SAR_SDU; - pi->partial_sdu_len = skb->len; + chan->conn_state |= L2CAP_CONN_SAR_SDU; + chan->partial_sdu_len = skb->len; break; case L2CAP_SDU_CONTINUE: - if (!(pi->conn_state & L2CAP_CONN_SAR_SDU)) + if (!(chan->conn_state & L2CAP_CONN_SAR_SDU)) goto disconnect; - if (!pi->sdu) + if (!chan->sdu) goto disconnect; - pi->partial_sdu_len += skb->len; - if (pi->partial_sdu_len > pi->sdu_len) + chan->partial_sdu_len += skb->len; + if (chan->partial_sdu_len > chan->sdu_len) goto drop; - memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len); + memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); break; case L2CAP_SDU_END: - if (!(pi->conn_state & L2CAP_CONN_SAR_SDU)) + if (!(chan->conn_state & L2CAP_CONN_SAR_SDU)) goto disconnect; - if (!pi->sdu) + if (!chan->sdu) goto disconnect; - if (!(pi->conn_state & L2CAP_CONN_SAR_RETRY)) { - pi->partial_sdu_len += skb->len; + if (!(chan->conn_state & L2CAP_CONN_SAR_RETRY)) { + chan->partial_sdu_len += skb->len; - if (pi->partial_sdu_len > pi->imtu) + if (chan->partial_sdu_len > chan->imtu) goto drop; - if (pi->partial_sdu_len != pi->sdu_len) + if (chan->partial_sdu_len != chan->sdu_len) goto drop; - memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len); + memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); } - _skb = skb_clone(pi->sdu, GFP_ATOMIC); + _skb = skb_clone(chan->sdu, GFP_ATOMIC); if (!_skb) { - pi->conn_state |= L2CAP_CONN_SAR_RETRY; + chan->conn_state |= L2CAP_CONN_SAR_RETRY; return -ENOMEM; } - err = sock_queue_rcv_skb(sk, _skb); + err = sock_queue_rcv_skb(chan->sk, _skb); if (err < 0) { kfree_skb(_skb); - pi->conn_state |= L2CAP_CONN_SAR_RETRY; + chan->conn_state |= L2CAP_CONN_SAR_RETRY; return err; } - pi->conn_state &= ~L2CAP_CONN_SAR_RETRY; - pi->conn_state &= ~L2CAP_CONN_SAR_SDU; + chan->conn_state &= ~L2CAP_CONN_SAR_RETRY; + chan->conn_state &= ~L2CAP_CONN_SAR_SDU; - kfree_skb(pi->sdu); + kfree_skb(chan->sdu); break; } @@ -2878,51 +3016,50 @@ static int l2cap_ertm_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 c return 0; drop: - kfree_skb(pi->sdu); - pi->sdu = NULL; + kfree_skb(chan->sdu); + chan->sdu = NULL; disconnect: - l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); + l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); kfree_skb(skb); return 0; } -static int l2cap_try_push_rx_skb(struct sock *sk) +static int l2cap_try_push_rx_skb(struct l2cap_chan *chan) { - struct l2cap_pinfo *pi = l2cap_pi(sk); struct sk_buff *skb; u16 control; int err; - while ((skb = skb_dequeue(BUSY_QUEUE(sk)))) { + while ((skb = skb_dequeue(&chan->busy_q))) { control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT; - err = l2cap_ertm_reassembly_sdu(sk, skb, control); + err = l2cap_ertm_reassembly_sdu(chan, skb, control); if (err < 0) { - skb_queue_head(BUSY_QUEUE(sk), skb); + skb_queue_head(&chan->busy_q, skb); return -EBUSY; } - pi->buffer_seq = (pi->buffer_seq + 1) % 64; + chan->buffer_seq = (chan->buffer_seq + 1) % 64; } - if (!(pi->conn_state & L2CAP_CONN_RNR_SENT)) + if (!(chan->conn_state & L2CAP_CONN_RNR_SENT)) goto done; - control = pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; + control = chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; control |= L2CAP_SUPER_RCV_READY | L2CAP_CTRL_POLL; - l2cap_send_sframe(pi, control); - l2cap_pi(sk)->retry_count = 1; + l2cap_send_sframe(chan, control); + chan->retry_count = 1; - del_timer(&pi->retrans_timer); + del_timer(&chan->retrans_timer); __mod_monitor_timer(); - l2cap_pi(sk)->conn_state |= L2CAP_CONN_WAIT_F; + chan->conn_state |= L2CAP_CONN_WAIT_F; done: - pi->conn_state &= ~L2CAP_CONN_LOCAL_BUSY; - pi->conn_state &= ~L2CAP_CONN_RNR_SENT; + chan->conn_state &= ~L2CAP_CONN_LOCAL_BUSY; + chan->conn_state &= ~L2CAP_CONN_RNR_SENT; - BT_DBG("sk %p, Exit local busy", sk); + BT_DBG("chan %p, Exit local busy", chan); return 0; } @@ -2930,21 +3067,21 @@ done: static void l2cap_busy_work(struct work_struct *work) { DECLARE_WAITQUEUE(wait, current); - struct l2cap_pinfo *pi = - container_of(work, struct l2cap_pinfo, busy_work); - struct sock *sk = (struct sock *)pi; + struct l2cap_chan *chan = + container_of(work, struct l2cap_chan, busy_work); + struct sock *sk = chan->sk; int n_tries = 0, timeo = HZ/5, err; struct sk_buff *skb; lock_sock(sk); add_wait_queue(sk_sleep(sk), &wait); - while ((skb = skb_peek(BUSY_QUEUE(sk)))) { + while ((skb = skb_peek(&chan->busy_q))) { set_current_state(TASK_INTERRUPTIBLE); if (n_tries++ > L2CAP_LOCAL_BUSY_TRIES) { err = -EBUSY; - l2cap_send_disconn_req(pi->conn, sk, EBUSY); + l2cap_send_disconn_req(chan->conn, chan, EBUSY); break; } @@ -2964,7 +3101,7 @@ static void l2cap_busy_work(struct work_struct *work) if (err) break; - if (l2cap_try_push_rx_skb(sk) == 0) + if (l2cap_try_push_rx_skb(chan) == 0) break; } @@ -2974,48 +3111,46 @@ static void l2cap_busy_work(struct work_struct *work) release_sock(sk); } -static int l2cap_push_rx_skb(struct sock *sk, struct sk_buff *skb, u16 control) +static int l2cap_push_rx_skb(struct l2cap_chan *chan, struct sk_buff *skb, u16 control) { - struct l2cap_pinfo *pi = l2cap_pi(sk); int sctrl, err; - if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY) { + if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) { bt_cb(skb)->sar = control >> L2CAP_CTRL_SAR_SHIFT; - __skb_queue_tail(BUSY_QUEUE(sk), skb); - return l2cap_try_push_rx_skb(sk); + __skb_queue_tail(&chan->busy_q, skb); + return l2cap_try_push_rx_skb(chan); } - err = l2cap_ertm_reassembly_sdu(sk, skb, control); + err = l2cap_ertm_reassembly_sdu(chan, skb, control); if (err >= 0) { - pi->buffer_seq = (pi->buffer_seq + 1) % 64; + chan->buffer_seq = (chan->buffer_seq + 1) % 64; return err; } /* Busy Condition */ - BT_DBG("sk %p, Enter local busy", sk); + BT_DBG("chan %p, Enter local busy", chan); - pi->conn_state |= L2CAP_CONN_LOCAL_BUSY; + chan->conn_state |= L2CAP_CONN_LOCAL_BUSY; bt_cb(skb)->sar = control >> L2CAP_CTRL_SAR_SHIFT; - __skb_queue_tail(BUSY_QUEUE(sk), skb); + __skb_queue_tail(&chan->busy_q, skb); - sctrl = pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; + sctrl = chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; sctrl |= L2CAP_SUPER_RCV_NOT_READY; - l2cap_send_sframe(pi, sctrl); + l2cap_send_sframe(chan, sctrl); - pi->conn_state |= L2CAP_CONN_RNR_SENT; + chan->conn_state |= L2CAP_CONN_RNR_SENT; - del_timer(&pi->ack_timer); + del_timer(&chan->ack_timer); - queue_work(_busy_wq, &pi->busy_work); + queue_work(_busy_wq, &chan->busy_work); return err; } -static int l2cap_streaming_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 control) +static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u16 control) { - struct l2cap_pinfo *pi = l2cap_pi(sk); struct sk_buff *_skb; int err = -EINVAL; @@ -3026,80 +3161,80 @@ static int l2cap_streaming_reassembly_sdu(struct sock *sk, struct sk_buff *skb, switch (control & L2CAP_CTRL_SAR) { case L2CAP_SDU_UNSEGMENTED: - if (pi->conn_state & L2CAP_CONN_SAR_SDU) { - kfree_skb(pi->sdu); + if (chan->conn_state & L2CAP_CONN_SAR_SDU) { + kfree_skb(chan->sdu); break; } - err = sock_queue_rcv_skb(sk, skb); + err = sock_queue_rcv_skb(chan->sk, skb); if (!err) return 0; break; case L2CAP_SDU_START: - if (pi->conn_state & L2CAP_CONN_SAR_SDU) { - kfree_skb(pi->sdu); + if (chan->conn_state & L2CAP_CONN_SAR_SDU) { + kfree_skb(chan->sdu); break; } - pi->sdu_len = get_unaligned_le16(skb->data); + chan->sdu_len = get_unaligned_le16(skb->data); skb_pull(skb, 2); - if (pi->sdu_len > pi->imtu) { + if (chan->sdu_len > chan->imtu) { err = -EMSGSIZE; break; } - pi->sdu = bt_skb_alloc(pi->sdu_len, GFP_ATOMIC); - if (!pi->sdu) { + chan->sdu = bt_skb_alloc(chan->sdu_len, GFP_ATOMIC); + if (!chan->sdu) { err = -ENOMEM; break; } - memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len); + memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); - pi->conn_state |= L2CAP_CONN_SAR_SDU; - pi->partial_sdu_len = skb->len; + chan->conn_state |= L2CAP_CONN_SAR_SDU; + chan->partial_sdu_len = skb->len; err = 0; break; case L2CAP_SDU_CONTINUE: - if (!(pi->conn_state & L2CAP_CONN_SAR_SDU)) + if (!(chan->conn_state & L2CAP_CONN_SAR_SDU)) break; - memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len); + memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); - pi->partial_sdu_len += skb->len; - if (pi->partial_sdu_len > pi->sdu_len) - kfree_skb(pi->sdu); + chan->partial_sdu_len += skb->len; + if (chan->partial_sdu_len > chan->sdu_len) + kfree_skb(chan->sdu); else err = 0; break; case L2CAP_SDU_END: - if (!(pi->conn_state & L2CAP_CONN_SAR_SDU)) + if (!(chan->conn_state & L2CAP_CONN_SAR_SDU)) break; - memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len); + memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); - pi->conn_state &= ~L2CAP_CONN_SAR_SDU; - pi->partial_sdu_len += skb->len; + chan->conn_state &= ~L2CAP_CONN_SAR_SDU; + chan->partial_sdu_len += skb->len; - if (pi->partial_sdu_len > pi->imtu) + if (chan->partial_sdu_len > chan->imtu) goto drop; - if (pi->partial_sdu_len == pi->sdu_len) { - _skb = skb_clone(pi->sdu, GFP_ATOMIC); - err = sock_queue_rcv_skb(sk, _skb); + if (chan->partial_sdu_len == chan->sdu_len) { + _skb = skb_clone(chan->sdu, GFP_ATOMIC); + err = sock_queue_rcv_skb(chan->sk, _skb); if (err < 0) kfree_skb(_skb); } err = 0; drop: - kfree_skb(pi->sdu); + kfree_skb(chan->sdu); break; } @@ -3107,31 +3242,30 @@ drop: return err; } -static void l2cap_check_srej_gap(struct sock *sk, u8 tx_seq) +static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq) { struct sk_buff *skb; u16 control; - while ((skb = skb_peek(SREJ_QUEUE(sk)))) { + while ((skb = skb_peek(&chan->srej_q))) { if (bt_cb(skb)->tx_seq != tx_seq) break; - skb = skb_dequeue(SREJ_QUEUE(sk)); + skb = skb_dequeue(&chan->srej_q); control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT; - l2cap_ertm_reassembly_sdu(sk, skb, control); - l2cap_pi(sk)->buffer_seq_srej = - (l2cap_pi(sk)->buffer_seq_srej + 1) % 64; + l2cap_ertm_reassembly_sdu(chan, skb, control); + chan->buffer_seq_srej = + (chan->buffer_seq_srej + 1) % 64; tx_seq = (tx_seq + 1) % 64; } } -static void l2cap_resend_srejframe(struct sock *sk, u8 tx_seq) +static void l2cap_resend_srejframe(struct l2cap_chan *chan, u8 tx_seq) { - struct l2cap_pinfo *pi = l2cap_pi(sk); struct srej_list *l, *tmp; u16 control; - list_for_each_entry_safe(l, tmp, SREJ_LIST(sk), list) { + list_for_each_entry_safe(l, tmp, &chan->srej_l, list) { if (l->tx_seq == tx_seq) { list_del(&l->list); kfree(l); @@ -3139,107 +3273,105 @@ static void l2cap_resend_srejframe(struct sock *sk, u8 tx_seq) } control = L2CAP_SUPER_SELECT_REJECT; control |= l->tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; - l2cap_send_sframe(pi, control); + l2cap_send_sframe(chan, control); list_del(&l->list); - list_add_tail(&l->list, SREJ_LIST(sk)); + list_add_tail(&l->list, &chan->srej_l); } } -static void l2cap_send_srejframe(struct sock *sk, u8 tx_seq) +static void l2cap_send_srejframe(struct l2cap_chan *chan, u8 tx_seq) { - struct l2cap_pinfo *pi = l2cap_pi(sk); struct srej_list *new; u16 control; - while (tx_seq != pi->expected_tx_seq) { + while (tx_seq != chan->expected_tx_seq) { control = L2CAP_SUPER_SELECT_REJECT; - control |= pi->expected_tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; - l2cap_send_sframe(pi, control); + control |= chan->expected_tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; + l2cap_send_sframe(chan, control); new = kzalloc(sizeof(struct srej_list), GFP_ATOMIC); - new->tx_seq = pi->expected_tx_seq; - pi->expected_tx_seq = (pi->expected_tx_seq + 1) % 64; - list_add_tail(&new->list, SREJ_LIST(sk)); + new->tx_seq = chan->expected_tx_seq; + chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64; + list_add_tail(&new->list, &chan->srej_l); } - pi->expected_tx_seq = (pi->expected_tx_seq + 1) % 64; + chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64; } -static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, struct sk_buff *skb) +static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_control, struct sk_buff *skb) { - struct l2cap_pinfo *pi = l2cap_pi(sk); u8 tx_seq = __get_txseq(rx_control); u8 req_seq = __get_reqseq(rx_control); u8 sar = rx_control >> L2CAP_CTRL_SAR_SHIFT; int tx_seq_offset, expected_tx_seq_offset; - int num_to_ack = (pi->tx_win/6) + 1; + int num_to_ack = (chan->tx_win/6) + 1; int err = 0; - BT_DBG("sk %p len %d tx_seq %d rx_control 0x%4.4x", sk, skb->len, tx_seq, - rx_control); + BT_DBG("chan %p len %d tx_seq %d rx_control 0x%4.4x", chan, skb->len, + tx_seq, rx_control); if (L2CAP_CTRL_FINAL & rx_control && - l2cap_pi(sk)->conn_state & L2CAP_CONN_WAIT_F) { - del_timer(&pi->monitor_timer); - if (pi->unacked_frames > 0) + chan->conn_state & L2CAP_CONN_WAIT_F) { + del_timer(&chan->monitor_timer); + if (chan->unacked_frames > 0) __mod_retrans_timer(); - pi->conn_state &= ~L2CAP_CONN_WAIT_F; + chan->conn_state &= ~L2CAP_CONN_WAIT_F; } - pi->expected_ack_seq = req_seq; - l2cap_drop_acked_frames(sk); + chan->expected_ack_seq = req_seq; + l2cap_drop_acked_frames(chan); - if (tx_seq == pi->expected_tx_seq) + if (tx_seq == chan->expected_tx_seq) goto expected; - tx_seq_offset = (tx_seq - pi->buffer_seq) % 64; + tx_seq_offset = (tx_seq - chan->buffer_seq) % 64; if (tx_seq_offset < 0) tx_seq_offset += 64; /* invalid tx_seq */ - if (tx_seq_offset >= pi->tx_win) { - l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); + if (tx_seq_offset >= chan->tx_win) { + l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); goto drop; } - if (pi->conn_state == L2CAP_CONN_LOCAL_BUSY) + if (chan->conn_state == L2CAP_CONN_LOCAL_BUSY) goto drop; - if (pi->conn_state & L2CAP_CONN_SREJ_SENT) { + if (chan->conn_state & L2CAP_CONN_SREJ_SENT) { struct srej_list *first; - first = list_first_entry(SREJ_LIST(sk), + first = list_first_entry(&chan->srej_l, struct srej_list, list); if (tx_seq == first->tx_seq) { - l2cap_add_to_srej_queue(sk, skb, tx_seq, sar); - l2cap_check_srej_gap(sk, tx_seq); + l2cap_add_to_srej_queue(chan, skb, tx_seq, sar); + l2cap_check_srej_gap(chan, tx_seq); list_del(&first->list); kfree(first); - if (list_empty(SREJ_LIST(sk))) { - pi->buffer_seq = pi->buffer_seq_srej; - pi->conn_state &= ~L2CAP_CONN_SREJ_SENT; - l2cap_send_ack(pi); - BT_DBG("sk %p, Exit SREJ_SENT", sk); + if (list_empty(&chan->srej_l)) { + chan->buffer_seq = chan->buffer_seq_srej; + chan->conn_state &= ~L2CAP_CONN_SREJ_SENT; + l2cap_send_ack(chan); + BT_DBG("chan %p, Exit SREJ_SENT", chan); } } else { struct srej_list *l; /* duplicated tx_seq */ - if (l2cap_add_to_srej_queue(sk, skb, tx_seq, sar) < 0) + if (l2cap_add_to_srej_queue(chan, skb, tx_seq, sar) < 0) goto drop; - list_for_each_entry(l, SREJ_LIST(sk), list) { + list_for_each_entry(l, &chan->srej_l, list) { if (l->tx_seq == tx_seq) { - l2cap_resend_srejframe(sk, tx_seq); + l2cap_resend_srejframe(chan, tx_seq); return 0; } } - l2cap_send_srejframe(sk, tx_seq); + l2cap_send_srejframe(chan, tx_seq); } } else { expected_tx_seq_offset = - (pi->expected_tx_seq - pi->buffer_seq) % 64; + (chan->expected_tx_seq - chan->buffer_seq) % 64; if (expected_tx_seq_offset < 0) expected_tx_seq_offset += 64; @@ -3247,51 +3379,51 @@ static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, str if (tx_seq_offset < expected_tx_seq_offset) goto drop; - pi->conn_state |= L2CAP_CONN_SREJ_SENT; + chan->conn_state |= L2CAP_CONN_SREJ_SENT; - BT_DBG("sk %p, Enter SREJ", sk); + BT_DBG("chan %p, Enter SREJ", chan); - INIT_LIST_HEAD(SREJ_LIST(sk)); - pi->buffer_seq_srej = pi->buffer_seq; + INIT_LIST_HEAD(&chan->srej_l); + chan->buffer_seq_srej = chan->buffer_seq; - __skb_queue_head_init(SREJ_QUEUE(sk)); - __skb_queue_head_init(BUSY_QUEUE(sk)); - l2cap_add_to_srej_queue(sk, skb, tx_seq, sar); + __skb_queue_head_init(&chan->srej_q); + __skb_queue_head_init(&chan->busy_q); + l2cap_add_to_srej_queue(chan, skb, tx_seq, sar); - pi->conn_state |= L2CAP_CONN_SEND_PBIT; + chan->conn_state |= L2CAP_CONN_SEND_PBIT; - l2cap_send_srejframe(sk, tx_seq); + l2cap_send_srejframe(chan, tx_seq); - del_timer(&pi->ack_timer); + del_timer(&chan->ack_timer); } return 0; expected: - pi->expected_tx_seq = (pi->expected_tx_seq + 1) % 64; + chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64; - if (pi->conn_state & L2CAP_CONN_SREJ_SENT) { + if (chan->conn_state & L2CAP_CONN_SREJ_SENT) { bt_cb(skb)->tx_seq = tx_seq; bt_cb(skb)->sar = sar; - __skb_queue_tail(SREJ_QUEUE(sk), skb); + __skb_queue_tail(&chan->srej_q, skb); return 0; } - err = l2cap_push_rx_skb(sk, skb, rx_control); + err = l2cap_push_rx_skb(chan, skb, rx_control); if (err < 0) return 0; if (rx_control & L2CAP_CTRL_FINAL) { - if (pi->conn_state & L2CAP_CONN_REJ_ACT) - pi->conn_state &= ~L2CAP_CONN_REJ_ACT; + if (chan->conn_state & L2CAP_CONN_REJ_ACT) + chan->conn_state &= ~L2CAP_CONN_REJ_ACT; else - l2cap_retransmit_frames(sk); + l2cap_retransmit_frames(chan); } __mod_ack_timer(); - pi->num_acked = (pi->num_acked + 1) % num_to_ack; - if (pi->num_acked == num_to_ack - 1) - l2cap_send_ack(pi); + chan->num_acked = (chan->num_acked + 1) % num_to_ack; + if (chan->num_acked == num_to_ack - 1) + l2cap_send_ack(chan); return 0; @@ -3300,165 +3432,160 @@ drop: return 0; } -static inline void l2cap_data_channel_rrframe(struct sock *sk, u16 rx_control) +static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u16 rx_control) { - struct l2cap_pinfo *pi = l2cap_pi(sk); - - BT_DBG("sk %p, req_seq %d ctrl 0x%4.4x", sk, __get_reqseq(rx_control), + BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, __get_reqseq(rx_control), rx_control); - pi->expected_ack_seq = __get_reqseq(rx_control); - l2cap_drop_acked_frames(sk); + chan->expected_ack_seq = __get_reqseq(rx_control); + l2cap_drop_acked_frames(chan); if (rx_control & L2CAP_CTRL_POLL) { - pi->conn_state |= L2CAP_CONN_SEND_FBIT; - if (pi->conn_state & L2CAP_CONN_SREJ_SENT) { - if ((pi->conn_state & L2CAP_CONN_REMOTE_BUSY) && - (pi->unacked_frames > 0)) + chan->conn_state |= L2CAP_CONN_SEND_FBIT; + if (chan->conn_state & L2CAP_CONN_SREJ_SENT) { + if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) && + (chan->unacked_frames > 0)) __mod_retrans_timer(); - pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; - l2cap_send_srejtail(sk); + chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + l2cap_send_srejtail(chan); } else { - l2cap_send_i_or_rr_or_rnr(sk); + l2cap_send_i_or_rr_or_rnr(chan); } } else if (rx_control & L2CAP_CTRL_FINAL) { - pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; - if (pi->conn_state & L2CAP_CONN_REJ_ACT) - pi->conn_state &= ~L2CAP_CONN_REJ_ACT; + if (chan->conn_state & L2CAP_CONN_REJ_ACT) + chan->conn_state &= ~L2CAP_CONN_REJ_ACT; else - l2cap_retransmit_frames(sk); + l2cap_retransmit_frames(chan); } else { - if ((pi->conn_state & L2CAP_CONN_REMOTE_BUSY) && - (pi->unacked_frames > 0)) + if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) && + (chan->unacked_frames > 0)) __mod_retrans_timer(); - pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; - if (pi->conn_state & L2CAP_CONN_SREJ_SENT) - l2cap_send_ack(pi); + chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + if (chan->conn_state & L2CAP_CONN_SREJ_SENT) + l2cap_send_ack(chan); else - l2cap_ertm_send(sk); + l2cap_ertm_send(chan); } } -static inline void l2cap_data_channel_rejframe(struct sock *sk, u16 rx_control) +static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u16 rx_control) { - struct l2cap_pinfo *pi = l2cap_pi(sk); u8 tx_seq = __get_reqseq(rx_control); - BT_DBG("sk %p, req_seq %d ctrl 0x%4.4x", sk, tx_seq, rx_control); + BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control); - pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; - pi->expected_ack_seq = tx_seq; - l2cap_drop_acked_frames(sk); + chan->expected_ack_seq = tx_seq; + l2cap_drop_acked_frames(chan); if (rx_control & L2CAP_CTRL_FINAL) { - if (pi->conn_state & L2CAP_CONN_REJ_ACT) - pi->conn_state &= ~L2CAP_CONN_REJ_ACT; + if (chan->conn_state & L2CAP_CONN_REJ_ACT) + chan->conn_state &= ~L2CAP_CONN_REJ_ACT; else - l2cap_retransmit_frames(sk); + l2cap_retransmit_frames(chan); } else { - l2cap_retransmit_frames(sk); + l2cap_retransmit_frames(chan); - if (pi->conn_state & L2CAP_CONN_WAIT_F) - pi->conn_state |= L2CAP_CONN_REJ_ACT; + if (chan->conn_state & L2CAP_CONN_WAIT_F) + chan->conn_state |= L2CAP_CONN_REJ_ACT; } } -static inline void l2cap_data_channel_srejframe(struct sock *sk, u16 rx_control) +static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_control) { - struct l2cap_pinfo *pi = l2cap_pi(sk); u8 tx_seq = __get_reqseq(rx_control); - BT_DBG("sk %p, req_seq %d ctrl 0x%4.4x", sk, tx_seq, rx_control); + BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control); - pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; if (rx_control & L2CAP_CTRL_POLL) { - pi->expected_ack_seq = tx_seq; - l2cap_drop_acked_frames(sk); + chan->expected_ack_seq = tx_seq; + l2cap_drop_acked_frames(chan); - pi->conn_state |= L2CAP_CONN_SEND_FBIT; - l2cap_retransmit_one_frame(sk, tx_seq); + chan->conn_state |= L2CAP_CONN_SEND_FBIT; + l2cap_retransmit_one_frame(chan, tx_seq); - l2cap_ertm_send(sk); + l2cap_ertm_send(chan); - if (pi->conn_state & L2CAP_CONN_WAIT_F) { - pi->srej_save_reqseq = tx_seq; - pi->conn_state |= L2CAP_CONN_SREJ_ACT; + if (chan->conn_state & L2CAP_CONN_WAIT_F) { + chan->srej_save_reqseq = tx_seq; + chan->conn_state |= L2CAP_CONN_SREJ_ACT; } } else if (rx_control & L2CAP_CTRL_FINAL) { - if ((pi->conn_state & L2CAP_CONN_SREJ_ACT) && - pi->srej_save_reqseq == tx_seq) - pi->conn_state &= ~L2CAP_CONN_SREJ_ACT; + if ((chan->conn_state & L2CAP_CONN_SREJ_ACT) && + chan->srej_save_reqseq == tx_seq) + chan->conn_state &= ~L2CAP_CONN_SREJ_ACT; else - l2cap_retransmit_one_frame(sk, tx_seq); + l2cap_retransmit_one_frame(chan, tx_seq); } else { - l2cap_retransmit_one_frame(sk, tx_seq); - if (pi->conn_state & L2CAP_CONN_WAIT_F) { - pi->srej_save_reqseq = tx_seq; - pi->conn_state |= L2CAP_CONN_SREJ_ACT; + l2cap_retransmit_one_frame(chan, tx_seq); + if (chan->conn_state & L2CAP_CONN_WAIT_F) { + chan->srej_save_reqseq = tx_seq; + chan->conn_state |= L2CAP_CONN_SREJ_ACT; } } } -static inline void l2cap_data_channel_rnrframe(struct sock *sk, u16 rx_control) +static inline void l2cap_data_channel_rnrframe(struct l2cap_chan *chan, u16 rx_control) { - struct l2cap_pinfo *pi = l2cap_pi(sk); u8 tx_seq = __get_reqseq(rx_control); - BT_DBG("sk %p, req_seq %d ctrl 0x%4.4x", sk, tx_seq, rx_control); + BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control); - pi->conn_state |= L2CAP_CONN_REMOTE_BUSY; - pi->expected_ack_seq = tx_seq; - l2cap_drop_acked_frames(sk); + chan->conn_state |= L2CAP_CONN_REMOTE_BUSY; + chan->expected_ack_seq = tx_seq; + l2cap_drop_acked_frames(chan); if (rx_control & L2CAP_CTRL_POLL) - pi->conn_state |= L2CAP_CONN_SEND_FBIT; + chan->conn_state |= L2CAP_CONN_SEND_FBIT; - if (!(pi->conn_state & L2CAP_CONN_SREJ_SENT)) { - del_timer(&pi->retrans_timer); + if (!(chan->conn_state & L2CAP_CONN_SREJ_SENT)) { + del_timer(&chan->retrans_timer); if (rx_control & L2CAP_CTRL_POLL) - l2cap_send_rr_or_rnr(pi, L2CAP_CTRL_FINAL); + l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_FINAL); return; } if (rx_control & L2CAP_CTRL_POLL) - l2cap_send_srejtail(sk); + l2cap_send_srejtail(chan); else - l2cap_send_sframe(pi, L2CAP_SUPER_RCV_READY); + l2cap_send_sframe(chan, L2CAP_SUPER_RCV_READY); } -static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, struct sk_buff *skb) +static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u16 rx_control, struct sk_buff *skb) { - BT_DBG("sk %p rx_control 0x%4.4x len %d", sk, rx_control, skb->len); + BT_DBG("chan %p rx_control 0x%4.4x len %d", chan, rx_control, skb->len); if (L2CAP_CTRL_FINAL & rx_control && - l2cap_pi(sk)->conn_state & L2CAP_CONN_WAIT_F) { - del_timer(&l2cap_pi(sk)->monitor_timer); - if (l2cap_pi(sk)->unacked_frames > 0) + chan->conn_state & L2CAP_CONN_WAIT_F) { + del_timer(&chan->monitor_timer); + if (chan->unacked_frames > 0) __mod_retrans_timer(); - l2cap_pi(sk)->conn_state &= ~L2CAP_CONN_WAIT_F; + chan->conn_state &= ~L2CAP_CONN_WAIT_F; } switch (rx_control & L2CAP_CTRL_SUPERVISE) { case L2CAP_SUPER_RCV_READY: - l2cap_data_channel_rrframe(sk, rx_control); + l2cap_data_channel_rrframe(chan, rx_control); break; case L2CAP_SUPER_REJECT: - l2cap_data_channel_rejframe(sk, rx_control); + l2cap_data_channel_rejframe(chan, rx_control); break; case L2CAP_SUPER_SELECT_REJECT: - l2cap_data_channel_srejframe(sk, rx_control); + l2cap_data_channel_srejframe(chan, rx_control); break; case L2CAP_SUPER_RCV_NOT_READY: - l2cap_data_channel_rnrframe(sk, rx_control); + l2cap_data_channel_rnrframe(chan, rx_control); break; } @@ -3468,7 +3595,7 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb) { - struct l2cap_pinfo *pi = l2cap_pi(sk); + struct l2cap_chan *chan = l2cap_pi(sk)->chan; u16 control; u8 req_seq; int len, next_tx_seq_offset, req_seq_offset; @@ -3482,51 +3609,51 @@ static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb) * Receiver will miss it and start proper recovery * procedures and ask retransmission. */ - if (l2cap_check_fcs(pi, skb)) + if (l2cap_check_fcs(chan, skb)) goto drop; if (__is_sar_start(control) && __is_iframe(control)) len -= 2; - if (pi->fcs == L2CAP_FCS_CRC16) + if (chan->fcs == L2CAP_FCS_CRC16) len -= 2; - if (len > pi->mps) { - l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); + if (len > chan->mps) { + l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); goto drop; } req_seq = __get_reqseq(control); - req_seq_offset = (req_seq - pi->expected_ack_seq) % 64; + req_seq_offset = (req_seq - chan->expected_ack_seq) % 64; if (req_seq_offset < 0) req_seq_offset += 64; next_tx_seq_offset = - (pi->next_tx_seq - pi->expected_ack_seq) % 64; + (chan->next_tx_seq - chan->expected_ack_seq) % 64; if (next_tx_seq_offset < 0) next_tx_seq_offset += 64; /* check for invalid req-seq */ if (req_seq_offset > next_tx_seq_offset) { - l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); + l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); goto drop; } if (__is_iframe(control)) { if (len < 0) { - l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); + l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); goto drop; } - l2cap_data_channel_iframe(sk, control, skb); + l2cap_data_channel_iframe(chan, control, skb); } else { if (len != 0) { BT_ERR("%d", len); - l2cap_send_disconn_req(pi->conn, sk, ECONNRESET); + l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); goto drop; } - l2cap_data_channel_sframe(sk, control, skb); + l2cap_data_channel_sframe(chan, control, skb); } return 0; @@ -3538,33 +3665,35 @@ drop: static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb) { - struct sock *sk; + struct l2cap_chan *chan; + struct sock *sk = NULL; struct l2cap_pinfo *pi; u16 control; u8 tx_seq; int len; - sk = l2cap_get_chan_by_scid(&conn->chan_list, cid); - if (!sk) { + chan = l2cap_get_chan_by_scid(conn, cid); + if (!chan) { BT_DBG("unknown cid 0x%4.4x", cid); goto drop; } + sk = chan->sk; pi = l2cap_pi(sk); - BT_DBG("sk %p, len %d", sk, skb->len); + BT_DBG("chan %p, len %d", chan, skb->len); if (sk->sk_state != BT_CONNECTED) goto drop; - switch (pi->mode) { + switch (chan->mode) { case L2CAP_MODE_BASIC: /* If socket recv buffers overflows we drop data here * which is *bad* because L2CAP has to be reliable. * But we don't have any other choice. L2CAP doesn't * provide flow control mechanism. */ - if (pi->imtu < skb->len) + if (chan->imtu < skb->len) goto drop; if (!sock_queue_rcv_skb(sk, skb)) @@ -3586,31 +3715,31 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk skb_pull(skb, 2); len = skb->len; - if (l2cap_check_fcs(pi, skb)) + if (l2cap_check_fcs(chan, skb)) goto drop; if (__is_sar_start(control)) len -= 2; - if (pi->fcs == L2CAP_FCS_CRC16) + if (chan->fcs == L2CAP_FCS_CRC16) len -= 2; - if (len > pi->mps || len < 0 || __is_sframe(control)) + if (len > chan->mps || len < 0 || __is_sframe(control)) goto drop; tx_seq = __get_txseq(control); - if (pi->expected_tx_seq == tx_seq) - pi->expected_tx_seq = (pi->expected_tx_seq + 1) % 64; + if (chan->expected_tx_seq == tx_seq) + chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64; else - pi->expected_tx_seq = (tx_seq + 1) % 64; + chan->expected_tx_seq = (tx_seq + 1) % 64; - l2cap_streaming_reassembly_sdu(sk, skb, control); + l2cap_streaming_reassembly_sdu(chan, skb, control); goto done; default: - BT_DBG("sk %p: bad mode 0x%2.2x", sk, pi->mode); + BT_DBG("chan %p: bad mode 0x%2.2x", chan, chan->mode); break; } @@ -3626,12 +3755,48 @@ done: static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, struct sk_buff *skb) { - struct sock *sk; + struct sock *sk = NULL; + struct l2cap_chan *chan; - sk = l2cap_get_sock_by_psm(0, psm, conn->src); - if (!sk) + chan = l2cap_global_chan_by_psm(0, psm, conn->src); + if (!chan) + goto drop; + + sk = chan->sk; + + bh_lock_sock(sk); + + BT_DBG("sk %p, len %d", sk, skb->len); + + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_CONNECTED) goto drop; + if (l2cap_pi(sk)->chan->imtu < skb->len) + goto drop; + + if (!sock_queue_rcv_skb(sk, skb)) + goto done; + +drop: + kfree_skb(skb); + +done: + if (sk) + bh_unlock_sock(sk); + return 0; +} + +static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, struct sk_buff *skb) +{ + struct sock *sk = NULL; + struct l2cap_chan *chan; + + chan = l2cap_global_chan_by_scid(0, cid, conn->src); + if (!chan) + goto drop; + + sk = chan->sk; + bh_lock_sock(sk); BT_DBG("sk %p, len %d", sk, skb->len); @@ -3639,7 +3804,7 @@ static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, str if (sk->sk_state != BT_BOUND && sk->sk_state != BT_CONNECTED) goto drop; - if (l2cap_pi(sk)->imtu < skb->len) + if (l2cap_pi(sk)->chan->imtu < skb->len) goto drop; if (!sock_queue_rcv_skb(sk, skb)) @@ -3683,6 +3848,10 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) l2cap_conless_channel(conn, psm, skb); break; + case L2CAP_CID_LE_DATA: + l2cap_att_channel(conn, cid, skb); + break; + default: l2cap_data_channel(conn, cid, skb); break; @@ -3694,8 +3863,7 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) { int exact = 0, lm1 = 0, lm2 = 0; - register struct sock *sk; - struct hlist_node *node; + struct l2cap_chan *c; if (type != ACL_LINK) return -EINVAL; @@ -3703,23 +3871,25 @@ static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr)); /* Find listening sockets and check their link_mode */ - read_lock(&l2cap_sk_list.lock); - sk_for_each(sk, node, &l2cap_sk_list.head) { + read_lock(&chan_list_lock); + list_for_each_entry(c, &chan_list, global_l) { + struct sock *sk = c->sk; + if (sk->sk_state != BT_LISTEN) continue; if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) { lm1 |= HCI_LM_ACCEPT; - if (l2cap_pi(sk)->role_switch) + if (c->role_switch) lm1 |= HCI_LM_MASTER; exact++; } else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) { lm2 |= HCI_LM_ACCEPT; - if (l2cap_pi(sk)->role_switch) + if (c->role_switch) lm2 |= HCI_LM_MASTER; } } - read_unlock(&l2cap_sk_list.lock); + read_unlock(&chan_list_lock); return exact ? lm1 : lm2; } @@ -3767,49 +3937,50 @@ static int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason) return 0; } -static inline void l2cap_check_encryption(struct sock *sk, u8 encrypt) +static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt) { + struct sock *sk = chan->sk; + if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM) return; if (encrypt == 0x00) { - if (l2cap_pi(sk)->sec_level == BT_SECURITY_MEDIUM) { + if (chan->sec_level == BT_SECURITY_MEDIUM) { l2cap_sock_clear_timer(sk); l2cap_sock_set_timer(sk, HZ * 5); - } else if (l2cap_pi(sk)->sec_level == BT_SECURITY_HIGH) + } else if (chan->sec_level == BT_SECURITY_HIGH) __l2cap_sock_close(sk, ECONNREFUSED); } else { - if (l2cap_pi(sk)->sec_level == BT_SECURITY_MEDIUM) + if (chan->sec_level == BT_SECURITY_MEDIUM) l2cap_sock_clear_timer(sk); } } static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) { - struct l2cap_chan_list *l; struct l2cap_conn *conn = hcon->l2cap_data; - struct sock *sk; + struct l2cap_chan *chan; if (!conn) return 0; - l = &conn->chan_list; - BT_DBG("conn %p", conn); - read_lock(&l->lock); + read_lock(&conn->chan_lock); + + list_for_each_entry(chan, &conn->chan_l, list) { + struct sock *sk = chan->sk; - for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { bh_lock_sock(sk); - if (l2cap_pi(sk)->conf_state & L2CAP_CONF_CONNECT_PEND) { + if (chan->conf_state & L2CAP_CONF_CONNECT_PEND) { bh_unlock_sock(sk); continue; } if (!status && (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG)) { - l2cap_check_encryption(sk, encrypt); + l2cap_check_encryption(chan, encrypt); bh_unlock_sock(sk); continue; } @@ -3817,13 +3988,13 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) if (sk->sk_state == BT_CONNECT) { if (!status) { struct l2cap_conn_req req; - req.scid = cpu_to_le16(l2cap_pi(sk)->scid); - req.psm = l2cap_pi(sk)->psm; + req.scid = cpu_to_le16(chan->scid); + req.psm = chan->psm; - l2cap_pi(sk)->ident = l2cap_get_ident(conn); - l2cap_pi(sk)->conf_state |= L2CAP_CONF_CONNECT_PEND; + chan->ident = l2cap_get_ident(conn); + chan->conf_state |= L2CAP_CONF_CONNECT_PEND; - l2cap_send_cmd(conn, l2cap_pi(sk)->ident, + l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req); } else { l2cap_sock_clear_timer(sk); @@ -3842,18 +4013,18 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) result = L2CAP_CR_SEC_BLOCK; } - rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); - rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); + rsp.scid = cpu_to_le16(chan->dcid); + rsp.dcid = cpu_to_le16(chan->scid); rsp.result = cpu_to_le16(result); rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); - l2cap_send_cmd(conn, l2cap_pi(sk)->ident, - L2CAP_CONN_RSP, sizeof(rsp), &rsp); + l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, + sizeof(rsp), &rsp); } bh_unlock_sock(sk); } - read_unlock(&l->lock); + read_unlock(&conn->chan_lock); return 0; } @@ -3872,7 +4043,7 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 fl if (!(flags & ACL_CONT)) { struct l2cap_hdr *hdr; - struct sock *sk; + struct l2cap_chan *chan; u16 cid; int len; @@ -3910,18 +4081,21 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 fl goto drop; } - sk = l2cap_get_chan_by_scid(&conn->chan_list, cid); + chan = l2cap_get_chan_by_scid(conn, cid); - if (sk && l2cap_pi(sk)->imtu < len - L2CAP_HDR_SIZE) { - BT_ERR("Frame exceeding recv MTU (len %d, MTU %d)", - len, l2cap_pi(sk)->imtu); - bh_unlock_sock(sk); - l2cap_conn_unreliable(conn, ECOMM); - goto drop; - } + if (chan && chan->sk) { + struct sock *sk = chan->sk; - if (sk) + if (chan->imtu < len - L2CAP_HDR_SIZE) { + BT_ERR("Frame exceeding recv MTU (len %d, " + "MTU %d)", len, + chan->imtu); + bh_unlock_sock(sk); + l2cap_conn_unreliable(conn, ECOMM); + goto drop; + } bh_unlock_sock(sk); + } /* Allocate skb for the complete frame (with header) */ conn->rx_skb = bt_skb_alloc(len, GFP_ATOMIC); @@ -3968,24 +4142,22 @@ drop: static int l2cap_debugfs_show(struct seq_file *f, void *p) { - struct sock *sk; - struct hlist_node *node; + struct l2cap_chan *c; - read_lock_bh(&l2cap_sk_list.lock); + read_lock_bh(&chan_list_lock); - sk_for_each(sk, node, &l2cap_sk_list.head) { - struct l2cap_pinfo *pi = l2cap_pi(sk); + list_for_each_entry(c, &chan_list, global_l) { + struct sock *sk = c->sk; seq_printf(f, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d %d\n", batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst), - sk->sk_state, __le16_to_cpu(pi->psm), - pi->scid, pi->dcid, - pi->imtu, pi->omtu, pi->sec_level, - pi->mode); + sk->sk_state, __le16_to_cpu(c->psm), + c->scid, c->dcid, c->imtu, c->omtu, + c->sec_level, c->mode); } - read_unlock_bh(&l2cap_sk_list.lock); + read_unlock_bh(&chan_list_lock); return 0; } diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 299fe56a966..18dc9888d8c 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -30,6 +30,8 @@ #include <net/bluetooth/hci_core.h> #include <net/bluetooth/l2cap.h> +static const struct proto_ops l2cap_sock_ops; + /* ---- L2CAP timers ---- */ static void l2cap_sock_timeout(unsigned long arg) { @@ -51,7 +53,7 @@ static void l2cap_sock_timeout(unsigned long arg) if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG) reason = ECONNREFUSED; else if (sk->sk_state == BT_CONNECT && - l2cap_pi(sk)->sec_level != BT_SECURITY_SDP) + l2cap_pi(sk)->chan->sec_level != BT_SECURITY_SDP) reason = ECONNREFUSED; else reason = ETIMEDOUT; @@ -76,21 +78,10 @@ void l2cap_sock_clear_timer(struct sock *sk) sk_stop_timer(sk, &sk->sk_timer); } -static struct sock *__l2cap_get_sock_by_addr(__le16 psm, bdaddr_t *src) -{ - struct sock *sk; - struct hlist_node *node; - sk_for_each(sk, node, &l2cap_sk_list.head) - if (l2cap_pi(sk)->sport == psm && !bacmp(&bt_sk(sk)->src, src)) - goto found; - sk = NULL; -found: - return sk; -} - static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) { struct sock *sk = sock->sk; + struct l2cap_chan *chan = l2cap_pi(sk)->chan; struct sockaddr_l2 la; int len, err = 0; @@ -129,26 +120,20 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) } } - write_lock_bh(&l2cap_sk_list.lock); + if (la.l2_cid) + err = l2cap_add_scid(chan, la.l2_cid); + else + err = l2cap_add_psm(chan, &la.l2_bdaddr, la.l2_psm); - if (la.l2_psm && __l2cap_get_sock_by_addr(la.l2_psm, &la.l2_bdaddr)) { - err = -EADDRINUSE; - } else { - /* Save source address */ - bacpy(&bt_sk(sk)->src, &la.l2_bdaddr); - l2cap_pi(sk)->psm = la.l2_psm; - l2cap_pi(sk)->sport = la.l2_psm; - sk->sk_state = BT_BOUND; - - if (__le16_to_cpu(la.l2_psm) == 0x0001 || - __le16_to_cpu(la.l2_psm) == 0x0003) - l2cap_pi(sk)->sec_level = BT_SECURITY_SDP; - } + if (err < 0) + goto done; - if (la.l2_cid) - l2cap_pi(sk)->scid = la.l2_cid; + if (__le16_to_cpu(la.l2_psm) == 0x0001 || + __le16_to_cpu(la.l2_psm) == 0x0003) + chan->sec_level = BT_SECURITY_SDP; - write_unlock_bh(&l2cap_sk_list.lock); + bacpy(&bt_sk(sk)->src, &la.l2_bdaddr); + sk->sk_state = BT_BOUND; done: release_sock(sk); @@ -158,6 +143,7 @@ done: static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) { struct sock *sk = sock->sk; + struct l2cap_chan *chan = l2cap_pi(sk)->chan; struct sockaddr_l2 la; int len, err = 0; @@ -182,7 +168,7 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al goto done; } - switch (l2cap_pi(sk)->mode) { + switch (chan->mode) { case L2CAP_MODE_BASIC: break; case L2CAP_MODE_ERTM: @@ -226,10 +212,10 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al /* Set destination address and psm */ bacpy(&bt_sk(sk)->dst, &la.l2_bdaddr); - l2cap_pi(sk)->psm = la.l2_psm; - l2cap_pi(sk)->dcid = la.l2_cid; + chan->psm = la.l2_psm; + chan->dcid = la.l2_cid; - err = l2cap_do_connect(sk); + err = l2cap_chan_connect(l2cap_pi(sk)->chan); if (err) goto done; @@ -244,6 +230,7 @@ done: static int l2cap_sock_listen(struct socket *sock, int backlog) { struct sock *sk = sock->sk; + struct l2cap_chan *chan = l2cap_pi(sk)->chan; int err = 0; BT_DBG("sk %p backlog %d", sk, backlog); @@ -256,7 +243,7 @@ static int l2cap_sock_listen(struct socket *sock, int backlog) goto done; } - switch (l2cap_pi(sk)->mode) { + switch (chan->mode) { case L2CAP_MODE_BASIC: break; case L2CAP_MODE_ERTM: @@ -269,28 +256,6 @@ static int l2cap_sock_listen(struct socket *sock, int backlog) goto done; } - if (!l2cap_pi(sk)->psm && !l2cap_pi(sk)->dcid) { - bdaddr_t *src = &bt_sk(sk)->src; - u16 psm; - - err = -EINVAL; - - write_lock_bh(&l2cap_sk_list.lock); - - for (psm = 0x1001; psm < 0x1100; psm += 2) - if (!__l2cap_get_sock_by_addr(cpu_to_le16(psm), src)) { - l2cap_pi(sk)->psm = cpu_to_le16(psm); - l2cap_pi(sk)->sport = cpu_to_le16(psm); - err = 0; - break; - } - - write_unlock_bh(&l2cap_sk_list.lock); - - if (err < 0) - goto done; - } - sk->sk_max_ack_backlog = backlog; sk->sk_ack_backlog = 0; sk->sk_state = BT_LISTEN; @@ -360,6 +325,7 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l { struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr; struct sock *sk = sock->sk; + struct l2cap_chan *chan = l2cap_pi(sk)->chan; BT_DBG("sock %p, sk %p", sock, sk); @@ -367,13 +333,13 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l *len = sizeof(struct sockaddr_l2); if (peer) { - la->l2_psm = l2cap_pi(sk)->psm; + la->l2_psm = chan->psm; bacpy(&la->l2_bdaddr, &bt_sk(sk)->dst); - la->l2_cid = cpu_to_le16(l2cap_pi(sk)->dcid); + la->l2_cid = cpu_to_le16(chan->dcid); } else { - la->l2_psm = l2cap_pi(sk)->sport; + la->l2_psm = chan->sport; bacpy(&la->l2_bdaddr, &bt_sk(sk)->src); - la->l2_cid = cpu_to_le16(l2cap_pi(sk)->scid); + la->l2_cid = cpu_to_le16(chan->scid); } return 0; @@ -382,6 +348,7 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; + struct l2cap_chan *chan = l2cap_pi(sk)->chan; struct l2cap_options opts; struct l2cap_conninfo cinfo; int len, err = 0; @@ -397,13 +364,13 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us switch (optname) { case L2CAP_OPTIONS: memset(&opts, 0, sizeof(opts)); - opts.imtu = l2cap_pi(sk)->imtu; - opts.omtu = l2cap_pi(sk)->omtu; - opts.flush_to = l2cap_pi(sk)->flush_to; - opts.mode = l2cap_pi(sk)->mode; - opts.fcs = l2cap_pi(sk)->fcs; - opts.max_tx = l2cap_pi(sk)->max_tx; - opts.txwin_size = (__u16)l2cap_pi(sk)->tx_win; + opts.imtu = chan->imtu; + opts.omtu = chan->omtu; + opts.flush_to = chan->flush_to; + opts.mode = chan->mode; + opts.fcs = chan->fcs; + opts.max_tx = chan->max_tx; + opts.txwin_size = (__u16)chan->tx_win; len = min_t(unsigned int, len, sizeof(opts)); if (copy_to_user(optval, (char *) &opts, len)) @@ -412,7 +379,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us break; case L2CAP_LM: - switch (l2cap_pi(sk)->sec_level) { + switch (chan->sec_level) { case BT_SECURITY_LOW: opt = L2CAP_LM_AUTH; break; @@ -428,10 +395,10 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us break; } - if (l2cap_pi(sk)->role_switch) + if (chan->role_switch) opt |= L2CAP_LM_MASTER; - if (l2cap_pi(sk)->force_reliable) + if (chan->force_reliable) opt |= L2CAP_LM_RELIABLE; if (put_user(opt, (u32 __user *) optval)) @@ -446,8 +413,8 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us break; } - cinfo.hci_handle = l2cap_pi(sk)->conn->hcon->handle; - memcpy(cinfo.dev_class, l2cap_pi(sk)->conn->hcon->dev_class, 3); + cinfo.hci_handle = chan->conn->hcon->handle; + memcpy(cinfo.dev_class, chan->conn->hcon->dev_class, 3); len = min_t(unsigned int, len, sizeof(cinfo)); if (copy_to_user(optval, (char *) &cinfo, len)) @@ -467,6 +434,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; + struct l2cap_chan *chan = l2cap_pi(sk)->chan; struct bt_security sec; int len, err = 0; @@ -491,7 +459,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch break; } - sec.level = l2cap_pi(sk)->sec_level; + sec.level = chan->sec_level; len = min_t(unsigned int, len, sizeof(sec)); if (copy_to_user(optval, (char *) &sec, len)) @@ -511,7 +479,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch break; case BT_FLUSHABLE: - if (put_user(l2cap_pi(sk)->flushable, (u32 __user *) optval)) + if (put_user(chan->flushable, (u32 __user *) optval)) err = -EFAULT; break; @@ -528,6 +496,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, unsigned int optlen) { struct sock *sk = sock->sk; + struct l2cap_chan *chan = l2cap_pi(sk)->chan; struct l2cap_options opts; int len, err = 0; u32 opt; @@ -543,13 +512,13 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us break; } - opts.imtu = l2cap_pi(sk)->imtu; - opts.omtu = l2cap_pi(sk)->omtu; - opts.flush_to = l2cap_pi(sk)->flush_to; - opts.mode = l2cap_pi(sk)->mode; - opts.fcs = l2cap_pi(sk)->fcs; - opts.max_tx = l2cap_pi(sk)->max_tx; - opts.txwin_size = (__u16)l2cap_pi(sk)->tx_win; + opts.imtu = chan->imtu; + opts.omtu = chan->omtu; + opts.flush_to = chan->flush_to; + opts.mode = chan->mode; + opts.fcs = chan->fcs; + opts.max_tx = chan->max_tx; + opts.txwin_size = (__u16)chan->tx_win; len = min_t(unsigned int, sizeof(opts), optlen); if (copy_from_user((char *) &opts, optval, len)) { @@ -562,10 +531,10 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us break; } - l2cap_pi(sk)->mode = opts.mode; - switch (l2cap_pi(sk)->mode) { + chan->mode = opts.mode; + switch (chan->mode) { case L2CAP_MODE_BASIC: - l2cap_pi(sk)->conf_state &= ~L2CAP_CONF_STATE2_DEVICE; + chan->conf_state &= ~L2CAP_CONF_STATE2_DEVICE; break; case L2CAP_MODE_ERTM: case L2CAP_MODE_STREAMING: @@ -577,11 +546,11 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us break; } - l2cap_pi(sk)->imtu = opts.imtu; - l2cap_pi(sk)->omtu = opts.omtu; - l2cap_pi(sk)->fcs = opts.fcs; - l2cap_pi(sk)->max_tx = opts.max_tx; - l2cap_pi(sk)->tx_win = (__u8)opts.txwin_size; + chan->imtu = opts.imtu; + chan->omtu = opts.omtu; + chan->fcs = opts.fcs; + chan->max_tx = opts.max_tx; + chan->tx_win = (__u8)opts.txwin_size; break; case L2CAP_LM: @@ -591,14 +560,14 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us } if (opt & L2CAP_LM_AUTH) - l2cap_pi(sk)->sec_level = BT_SECURITY_LOW; + chan->sec_level = BT_SECURITY_LOW; if (opt & L2CAP_LM_ENCRYPT) - l2cap_pi(sk)->sec_level = BT_SECURITY_MEDIUM; + chan->sec_level = BT_SECURITY_MEDIUM; if (opt & L2CAP_LM_SECURE) - l2cap_pi(sk)->sec_level = BT_SECURITY_HIGH; + chan->sec_level = BT_SECURITY_HIGH; - l2cap_pi(sk)->role_switch = (opt & L2CAP_LM_MASTER); - l2cap_pi(sk)->force_reliable = (opt & L2CAP_LM_RELIABLE); + chan->role_switch = (opt & L2CAP_LM_MASTER); + chan->force_reliable = (opt & L2CAP_LM_RELIABLE); break; default: @@ -613,6 +582,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen) { struct sock *sk = sock->sk; + struct l2cap_chan *chan = l2cap_pi(sk)->chan; struct bt_security sec; int len, err = 0; u32 opt; @@ -649,7 +619,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch break; } - l2cap_pi(sk)->sec_level = sec.level; + chan->sec_level = sec.level; break; case BT_DEFER_SETUP: @@ -678,7 +648,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch } if (opt == BT_FLUSHABLE_OFF) { - struct l2cap_conn *conn = l2cap_pi(sk)->conn; + struct l2cap_conn *conn = chan->conn; /* proceed further only when we have l2cap_conn and No Flush support in the LM */ if (!conn || !lmp_no_flush_capable(conn->hcon->hdev)) { @@ -687,7 +657,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch } } - l2cap_pi(sk)->flushable = opt; + chan->flushable = opt; break; default: @@ -702,7 +672,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; - struct l2cap_pinfo *pi = l2cap_pi(sk); + struct l2cap_chan *chan = l2cap_pi(sk)->chan; struct sk_buff *skb; u16 control; int err; @@ -725,74 +695,77 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms /* Connectionless channel */ if (sk->sk_type == SOCK_DGRAM) { - skb = l2cap_create_connless_pdu(sk, msg, len); + skb = l2cap_create_connless_pdu(chan, msg, len); if (IS_ERR(skb)) { err = PTR_ERR(skb); } else { - l2cap_do_send(sk, skb); + l2cap_do_send(chan, skb); err = len; } goto done; } - switch (pi->mode) { + switch (chan->mode) { case L2CAP_MODE_BASIC: /* Check outgoing MTU */ - if (len > pi->omtu) { + if (len > chan->omtu) { err = -EMSGSIZE; goto done; } /* Create a basic PDU */ - skb = l2cap_create_basic_pdu(sk, msg, len); + skb = l2cap_create_basic_pdu(chan, msg, len); if (IS_ERR(skb)) { err = PTR_ERR(skb); goto done; } - l2cap_do_send(sk, skb); + l2cap_do_send(chan, skb); err = len; break; case L2CAP_MODE_ERTM: case L2CAP_MODE_STREAMING: /* Entire SDU fits into one PDU */ - if (len <= pi->remote_mps) { + if (len <= chan->remote_mps) { control = L2CAP_SDU_UNSEGMENTED; - skb = l2cap_create_iframe_pdu(sk, msg, len, control, 0); + skb = l2cap_create_iframe_pdu(chan, msg, len, control, + 0); if (IS_ERR(skb)) { err = PTR_ERR(skb); goto done; } - __skb_queue_tail(TX_QUEUE(sk), skb); + __skb_queue_tail(&chan->tx_q, skb); - if (sk->sk_send_head == NULL) - sk->sk_send_head = skb; + if (chan->tx_send_head == NULL) + chan->tx_send_head = skb; } else { /* Segment SDU into multiples PDUs */ - err = l2cap_sar_segment_sdu(sk, msg, len); + err = l2cap_sar_segment_sdu(chan, msg, len); if (err < 0) goto done; } - if (pi->mode == L2CAP_MODE_STREAMING) { - l2cap_streaming_send(sk); - } else { - if ((pi->conn_state & L2CAP_CONN_REMOTE_BUSY) && - (pi->conn_state & L2CAP_CONN_WAIT_F)) { - err = len; - break; - } - err = l2cap_ertm_send(sk); + if (chan->mode == L2CAP_MODE_STREAMING) { + l2cap_streaming_send(chan); + err = len; + break; + } + + if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) && + (chan->conn_state & L2CAP_CONN_WAIT_F)) { + err = len; + break; } + err = l2cap_ertm_send(chan); if (err >= 0) err = len; break; default: - BT_DBG("bad state %1.1x", pi->mode); + BT_DBG("bad state %1.1x", chan->mode); err = -EBADFD; } @@ -808,29 +781,9 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct ms lock_sock(sk); if (sk->sk_state == BT_CONNECT2 && bt_sk(sk)->defer_setup) { - struct l2cap_conn_rsp rsp; - struct l2cap_conn *conn = l2cap_pi(sk)->conn; - u8 buf[128]; - sk->sk_state = BT_CONFIG; - rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); - rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); - rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS); - rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); - l2cap_send_cmd(l2cap_pi(sk)->conn, l2cap_pi(sk)->ident, - L2CAP_CONN_RSP, sizeof(rsp), &rsp); - - if (l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT) { - release_sock(sk); - return 0; - } - - l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT; - l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, - l2cap_build_conf_req(sk, buf), buf); - l2cap_pi(sk)->num_conf_req++; - + __l2cap_connect_rsp_defer(l2cap_pi(sk)->chan); release_sock(sk); return 0; } @@ -854,7 +807,8 @@ void l2cap_sock_kill(struct sock *sk) BT_DBG("sk %p state %d", sk, sk->sk_state); /* Kill poor orphan */ - bt_sock_unlink(&l2cap_sk_list, sk); + + l2cap_chan_destroy(l2cap_pi(sk)->chan); sock_set_flag(sk, SOCK_DEAD); sock_put(sk); } @@ -885,7 +839,8 @@ static void l2cap_sock_cleanup_listen(struct sock *parent) void __l2cap_sock_close(struct sock *sk, int reason) { - struct l2cap_conn *conn = l2cap_pi(sk)->conn; + struct l2cap_chan *chan = l2cap_pi(sk)->chan; + struct l2cap_conn *conn = chan->conn; BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket); @@ -900,9 +855,9 @@ void __l2cap_sock_close(struct sock *sk, int reason) sk->sk_type == SOCK_STREAM) && conn->hcon->type == ACL_LINK) { l2cap_sock_set_timer(sk, sk->sk_sndtimeo); - l2cap_send_disconn_req(conn, sk, reason); + l2cap_send_disconn_req(conn, chan, reason); } else - l2cap_chan_del(sk, reason); + l2cap_chan_del(chan, reason); break; case BT_CONNECT2: @@ -917,20 +872,20 @@ void __l2cap_sock_close(struct sock *sk, int reason) else result = L2CAP_CR_BAD_PSM; - rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); - rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); + rsp.scid = cpu_to_le16(chan->dcid); + rsp.dcid = cpu_to_le16(chan->scid); rsp.result = cpu_to_le16(result); rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); - l2cap_send_cmd(conn, l2cap_pi(sk)->ident, - L2CAP_CONN_RSP, sizeof(rsp), &rsp); + l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, + sizeof(rsp), &rsp); } - l2cap_chan_del(sk, reason); + l2cap_chan_del(chan, reason); break; case BT_CONNECT: case BT_DISCONN: - l2cap_chan_del(sk, reason); + l2cap_chan_del(chan, reason); break; default: @@ -942,6 +897,7 @@ void __l2cap_sock_close(struct sock *sk, int reason) static int l2cap_sock_shutdown(struct socket *sock, int how) { struct sock *sk = sock->sk; + struct l2cap_chan *chan = l2cap_pi(sk)->chan; int err = 0; BT_DBG("sock %p, sk %p", sock, sk); @@ -951,7 +907,7 @@ static int l2cap_sock_shutdown(struct socket *sock, int how) lock_sock(sk); if (!sk->sk_shutdown) { - if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) + if (chan->mode == L2CAP_MODE_ERTM) err = __l2cap_wait_ack(sk); sk->sk_shutdown = SHUTDOWN_MASK; @@ -998,49 +954,47 @@ static void l2cap_sock_destruct(struct sock *sk) void l2cap_sock_init(struct sock *sk, struct sock *parent) { struct l2cap_pinfo *pi = l2cap_pi(sk); + struct l2cap_chan *chan = pi->chan; BT_DBG("sk %p", sk); if (parent) { + struct l2cap_chan *pchan = l2cap_pi(parent)->chan; + sk->sk_type = parent->sk_type; bt_sk(sk)->defer_setup = bt_sk(parent)->defer_setup; - pi->imtu = l2cap_pi(parent)->imtu; - pi->omtu = l2cap_pi(parent)->omtu; - pi->conf_state = l2cap_pi(parent)->conf_state; - pi->mode = l2cap_pi(parent)->mode; - pi->fcs = l2cap_pi(parent)->fcs; - pi->max_tx = l2cap_pi(parent)->max_tx; - pi->tx_win = l2cap_pi(parent)->tx_win; - pi->sec_level = l2cap_pi(parent)->sec_level; - pi->role_switch = l2cap_pi(parent)->role_switch; - pi->force_reliable = l2cap_pi(parent)->force_reliable; - pi->flushable = l2cap_pi(parent)->flushable; + chan->imtu = pchan->imtu; + chan->omtu = pchan->omtu; + chan->conf_state = pchan->conf_state; + chan->mode = pchan->mode; + chan->fcs = pchan->fcs; + chan->max_tx = pchan->max_tx; + chan->tx_win = pchan->tx_win; + chan->sec_level = pchan->sec_level; + chan->role_switch = pchan->role_switch; + chan->force_reliable = pchan->force_reliable; + chan->flushable = pchan->flushable; } else { - pi->imtu = L2CAP_DEFAULT_MTU; - pi->omtu = 0; + chan->imtu = L2CAP_DEFAULT_MTU; + chan->omtu = 0; if (!disable_ertm && sk->sk_type == SOCK_STREAM) { - pi->mode = L2CAP_MODE_ERTM; - pi->conf_state |= L2CAP_CONF_STATE2_DEVICE; + chan->mode = L2CAP_MODE_ERTM; + chan->conf_state |= L2CAP_CONF_STATE2_DEVICE; } else { - pi->mode = L2CAP_MODE_BASIC; + chan->mode = L2CAP_MODE_BASIC; } - pi->max_tx = L2CAP_DEFAULT_MAX_TX; - pi->fcs = L2CAP_FCS_CRC16; - pi->tx_win = L2CAP_DEFAULT_TX_WINDOW; - pi->sec_level = BT_SECURITY_LOW; - pi->role_switch = 0; - pi->force_reliable = 0; - pi->flushable = BT_FLUSHABLE_OFF; + chan->max_tx = L2CAP_DEFAULT_MAX_TX; + chan->fcs = L2CAP_FCS_CRC16; + chan->tx_win = L2CAP_DEFAULT_TX_WINDOW; + chan->sec_level = BT_SECURITY_LOW; + chan->role_switch = 0; + chan->force_reliable = 0; + chan->flushable = BT_FLUSHABLE_OFF; } /* Default config options */ - pi->conf_len = 0; - pi->flush_to = L2CAP_DEFAULT_FLUSH_TO; - skb_queue_head_init(TX_QUEUE(sk)); - skb_queue_head_init(SREJ_QUEUE(sk)); - skb_queue_head_init(BUSY_QUEUE(sk)); - INIT_LIST_HEAD(SREJ_LIST(sk)); + chan->flush_to = L2CAP_DEFAULT_FLUSH_TO; } static struct proto l2cap_proto = { @@ -1070,7 +1024,6 @@ struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, g setup_timer(&sk->sk_timer, l2cap_sock_timeout, (unsigned long) sk); - bt_sock_link(&l2cap_sk_list, sk); return sk; } @@ -1078,6 +1031,7 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol, int kern) { struct sock *sk; + struct l2cap_chan *chan; BT_DBG("sock %p", sock); @@ -1096,11 +1050,19 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol, if (!sk) return -ENOMEM; + chan = l2cap_chan_create(sk); + if (!chan) { + l2cap_sock_kill(sk); + return -ENOMEM; + } + + l2cap_pi(sk)->chan = chan; + l2cap_sock_init(sk, NULL); return 0; } -const struct proto_ops l2cap_sock_ops = { +static const struct proto_ops l2cap_sock_ops = { .family = PF_BLUETOOTH, .owner = THIS_MODULE, .release = l2cap_sock_release, diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index c304688252b..dae382ce702 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -945,7 +945,7 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len) for (i = 0; i < key_count; i++) { struct mgmt_key_info *key = &cp->keys[i]; - hci_add_link_key(hdev, 0, &key->bdaddr, key->val, key->type, + hci_add_link_key(hdev, NULL, 0, &key->bdaddr, key->val, key->type, key->pin_len); } @@ -1033,6 +1033,9 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len) } conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); + if (!conn) + conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->bdaddr); + if (!conn) { err = cmd_status(sk, index, MGMT_OP_DISCONNECT, ENOTCONN); goto failed; @@ -1569,6 +1572,75 @@ static int remove_remote_oob_data(struct sock *sk, u16 index, return err; } +static int start_discovery(struct sock *sk, u16 index) +{ + u8 lap[3] = { 0x33, 0x8b, 0x9e }; + struct hci_cp_inquiry cp; + struct pending_cmd *cmd; + struct hci_dev *hdev; + int err; + + BT_DBG("hci%u", index); + + hdev = hci_dev_get(index); + if (!hdev) + return cmd_status(sk, index, MGMT_OP_START_DISCOVERY, ENODEV); + + hci_dev_lock_bh(hdev); + + cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, index, NULL, 0); + if (!cmd) { + err = -ENOMEM; + goto failed; + } + + memset(&cp, 0, sizeof(cp)); + memcpy(&cp.lap, lap, 3); + cp.length = 0x08; + cp.num_rsp = 0x00; + + err = hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp); + if (err < 0) + mgmt_pending_remove(cmd); + +failed: + hci_dev_unlock_bh(hdev); + hci_dev_put(hdev); + + return err; +} + +static int stop_discovery(struct sock *sk, u16 index) +{ + struct hci_dev *hdev; + struct pending_cmd *cmd; + int err; + + BT_DBG("hci%u", index); + + hdev = hci_dev_get(index); + if (!hdev) + return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY, ENODEV); + + hci_dev_lock_bh(hdev); + + cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, index, NULL, 0); + if (!cmd) { + err = -ENOMEM; + goto failed; + } + + err = hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL); + if (err < 0) + mgmt_pending_remove(cmd); + +failed: + hci_dev_unlock_bh(hdev); + hci_dev_put(hdev); + + return err; +} + int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) { unsigned char *buf; @@ -1677,7 +1749,12 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) err = remove_remote_oob_data(sk, index, buf + sizeof(*hdr), len); break; - + case MGMT_OP_START_DISCOVERY: + err = start_discovery(sk, index); + break; + case MGMT_OP_STOP_DISCOVERY: + err = stop_discovery(sk, index); + break; default: BT_DBG("Unknown op %u", opcode); err = cmd_status(sk, index, opcode, 0x01); @@ -1784,17 +1861,17 @@ int mgmt_connectable(u16 index, u8 connectable) return ret; } -int mgmt_new_key(u16 index, struct link_key *key, u8 old_key_type) +int mgmt_new_key(u16 index, struct link_key *key, u8 persistent) { struct mgmt_ev_new_key ev; memset(&ev, 0, sizeof(ev)); + ev.store_hint = persistent; bacpy(&ev.key.bdaddr, &key->bdaddr); ev.key.type = key->type; memcpy(ev.key.val, key->val, 16); ev.key.pin_len = key->pin_len; - ev.old_key_type = old_key_type; return mgmt_event(MGMT_EV_NEW_KEY, index, &ev, sizeof(ev), NULL); } @@ -1868,11 +1945,12 @@ int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 status) return mgmt_event(MGMT_EV_CONNECT_FAILED, index, &ev, sizeof(ev), NULL); } -int mgmt_pin_code_request(u16 index, bdaddr_t *bdaddr) +int mgmt_pin_code_request(u16 index, bdaddr_t *bdaddr, u8 secure) { struct mgmt_ev_pin_code_request ev; bacpy(&ev.bdaddr, bdaddr); + ev.secure = secure; return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, index, &ev, sizeof(ev), NULL); @@ -1920,13 +1998,15 @@ int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) return err; } -int mgmt_user_confirm_request(u16 index, bdaddr_t *bdaddr, __le32 value) +int mgmt_user_confirm_request(u16 index, bdaddr_t *bdaddr, __le32 value, + u8 confirm_hint) { struct mgmt_ev_user_confirm_request ev; BT_DBG("hci%u", index); bacpy(&ev.bdaddr, bdaddr); + ev.confirm_hint = confirm_hint; put_unaligned_le32(value, &ev.value); return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, index, &ev, sizeof(ev), @@ -2075,3 +2155,9 @@ int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name) return mgmt_event(MGMT_EV_REMOTE_NAME, index, &ev, sizeof(ev), NULL); } + +int mgmt_discovering(u16 index, u8 discovering) +{ + return mgmt_event(MGMT_EV_DISCOVERING, index, &discovering, + sizeof(discovering), NULL); +} diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index c9973932456..5759bb7054f 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -232,6 +232,8 @@ static int rfcomm_l2sock_create(struct socket **sock) static inline int rfcomm_check_security(struct rfcomm_dlc *d) { struct sock *sk = d->session->sock->sk; + struct l2cap_conn *conn = l2cap_pi(sk)->chan->conn; + __u8 auth_type; switch (d->sec_level) { @@ -246,8 +248,7 @@ static inline int rfcomm_check_security(struct rfcomm_dlc *d) break; } - return hci_conn_security(l2cap_pi(sk)->conn->hcon, d->sec_level, - auth_type); + return hci_conn_security(conn->hcon, d->sec_level, auth_type); } static void rfcomm_session_timeout(unsigned long arg) @@ -710,10 +711,10 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, /* Set L2CAP options */ sk = sock->sk; lock_sock(sk); - l2cap_pi(sk)->imtu = l2cap_mtu; - l2cap_pi(sk)->sec_level = sec_level; + l2cap_pi(sk)->chan->imtu = l2cap_mtu; + l2cap_pi(sk)->chan->sec_level = sec_level; if (l2cap_ertm) - l2cap_pi(sk)->mode = L2CAP_MODE_ERTM; + l2cap_pi(sk)->chan->mode = L2CAP_MODE_ERTM; release_sock(sk); s = rfcomm_session_add(sock, BT_BOUND); @@ -1241,6 +1242,7 @@ static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci) void rfcomm_dlc_accept(struct rfcomm_dlc *d) { struct sock *sk = d->session->sock->sk; + struct l2cap_conn *conn = l2cap_pi(sk)->chan->conn; BT_DBG("dlc %p", d); @@ -1254,7 +1256,7 @@ void rfcomm_dlc_accept(struct rfcomm_dlc *d) rfcomm_dlc_unlock(d); if (d->role_switch) - hci_conn_switch_role(l2cap_pi(sk)->conn->hcon, 0x00); + hci_conn_switch_role(conn->hcon, 0x00); rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig); } @@ -1890,7 +1892,8 @@ static inline void rfcomm_accept_connection(struct rfcomm_session *s) /* We should adjust MTU on incoming sessions. * L2CAP MTU minus UIH header and FCS. */ - s->mtu = min(l2cap_pi(nsock->sk)->omtu, l2cap_pi(nsock->sk)->imtu) - 5; + s->mtu = min(l2cap_pi(nsock->sk)->chan->omtu, + l2cap_pi(nsock->sk)->chan->imtu) - 5; rfcomm_schedule(); } else @@ -1909,7 +1912,7 @@ static inline void rfcomm_check_connection(struct rfcomm_session *s) /* We can adjust MTU on outgoing sessions. * L2CAP MTU minus UIH header and FCS. */ - s->mtu = min(l2cap_pi(sk)->omtu, l2cap_pi(sk)->imtu) - 5; + s->mtu = min(l2cap_pi(sk)->chan->omtu, l2cap_pi(sk)->chan->imtu) - 5; rfcomm_send_sabm(s, 0); break; @@ -1992,7 +1995,7 @@ static int rfcomm_add_listener(bdaddr_t *ba) /* Set L2CAP options */ sk = sock->sk; lock_sock(sk); - l2cap_pi(sk)->imtu = l2cap_mtu; + l2cap_pi(sk)->chan->imtu = l2cap_mtu; release_sock(sk); /* Start listening on the socket */ @@ -2093,7 +2096,7 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt) if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags)) continue; - if (!status) + if (!status && hci_conn_check_secure(conn, d->sec_level)) set_bit(RFCOMM_AUTH_ACCEPT, &d->flags); else set_bit(RFCOMM_AUTH_REJECT, &d->flags); diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index 66cc1f0c3df..386cfaffd4b 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -743,6 +743,7 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u struct sock *sk = sock->sk; struct sock *l2cap_sk; struct rfcomm_conninfo cinfo; + struct l2cap_conn *conn = l2cap_pi(sk)->chan->conn; int len, err = 0; u32 opt; @@ -787,8 +788,8 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u l2cap_sk = rfcomm_pi(sk)->dlc->session->sock->sk; - cinfo.hci_handle = l2cap_pi(l2cap_sk)->conn->hcon->handle; - memcpy(cinfo.dev_class, l2cap_pi(l2cap_sk)->conn->hcon->dev_class, 3); + cinfo.hci_handle = conn->hcon->handle; + memcpy(cinfo.dev_class, conn->hcon->dev_class, 3); len = min_t(unsigned int, len, sizeof(cinfo)); if (copy_to_user(optval, (char *) &cinfo, len)) diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 45cfd54b06d..a6b2f86378c 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -90,8 +90,7 @@ static int br_dev_open(struct net_device *dev) struct net_bridge *br = netdev_priv(dev); netif_carrier_off(dev); - - br_features_recompute(br); + netdev_update_features(dev); netif_start_queue(dev); br_stp_enable_bridge(br); br_multicast_open(br); @@ -188,48 +187,11 @@ static void br_getinfo(struct net_device *dev, struct ethtool_drvinfo *info) strcpy(info->bus_info, "N/A"); } -static int br_set_sg(struct net_device *dev, u32 data) -{ - struct net_bridge *br = netdev_priv(dev); - - if (data) - br->feature_mask |= NETIF_F_SG; - else - br->feature_mask &= ~NETIF_F_SG; - - br_features_recompute(br); - return 0; -} - -static int br_set_tso(struct net_device *dev, u32 data) +static u32 br_fix_features(struct net_device *dev, u32 features) { struct net_bridge *br = netdev_priv(dev); - if (data) - br->feature_mask |= NETIF_F_TSO; - else - br->feature_mask &= ~NETIF_F_TSO; - - br_features_recompute(br); - return 0; -} - -static int br_set_tx_csum(struct net_device *dev, u32 data) -{ - struct net_bridge *br = netdev_priv(dev); - - if (data) - br->feature_mask |= NETIF_F_NO_CSUM; - else - br->feature_mask &= ~NETIF_F_ALL_CSUM; - - br_features_recompute(br); - return 0; -} - -static int br_set_flags(struct net_device *netdev, u32 data) -{ - return ethtool_op_set_flags(netdev, data, ETH_FLAG_TXVLAN); + return br_features_recompute(br, features); } #ifdef CONFIG_NET_POLL_CONTROLLER @@ -330,16 +292,6 @@ static int br_del_slave(struct net_device *dev, struct net_device *slave_dev) static const struct ethtool_ops br_ethtool_ops = { .get_drvinfo = br_getinfo, .get_link = ethtool_op_get_link, - .get_tx_csum = ethtool_op_get_tx_csum, - .set_tx_csum = br_set_tx_csum, - .get_sg = ethtool_op_get_sg, - .set_sg = br_set_sg, - .get_tso = ethtool_op_get_tso, - .set_tso = br_set_tso, - .get_ufo = ethtool_op_get_ufo, - .set_ufo = ethtool_op_set_ufo, - .get_flags = ethtool_op_get_flags, - .set_flags = br_set_flags, }; static const struct net_device_ops br_netdev_ops = { @@ -359,6 +311,7 @@ static const struct net_device_ops br_netdev_ops = { #endif .ndo_add_slave = br_add_slave, .ndo_del_slave = br_del_slave, + .ndo_fix_features = br_fix_features, }; static void br_dev_free(struct net_device *dev) @@ -389,7 +342,10 @@ void br_dev_setup(struct net_device *dev) dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA | NETIF_F_GSO_MASK | NETIF_F_NO_CSUM | NETIF_F_LLTX | - NETIF_F_NETNS_LOCAL | NETIF_F_GSO | NETIF_F_HW_VLAN_TX; + NETIF_F_NETNS_LOCAL | NETIF_F_HW_VLAN_TX; + dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA | + NETIF_F_GSO_MASK | NETIF_F_NO_CSUM | + NETIF_F_HW_VLAN_TX; br->dev = dev; spin_lock_init(&br->lock); @@ -401,7 +357,6 @@ void br_dev_setup(struct net_device *dev) memcpy(br->group_addr, br_group_address, ETH_ALEN); - br->feature_mask = dev->features; br->stp_enabled = BR_NO_STP; br->designated_root = br->bridge_id; br->bridge_max_age = br->max_age = 20 * HZ; diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 7f5379c593d..1bacca4cb67 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -36,8 +36,8 @@ static int port_cost(struct net_device *dev) if (dev->ethtool_ops && dev->ethtool_ops->get_settings) { struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET, }; - if (!dev->ethtool_ops->get_settings(dev, &ecmd)) { - switch(ecmd.speed) { + if (!dev_ethtool_get_settings(dev, &ecmd)) { + switch (ethtool_cmd_speed(&ecmd)) { case SPEED_10000: return 2; case SPEED_1000: @@ -147,6 +147,7 @@ static void del_nbp(struct net_bridge_port *p) dev->priv_flags &= ~IFF_BRIDGE_PORT; netdev_rx_handler_unregister(dev); + synchronize_net(); netdev_set_master(dev, NULL); @@ -291,15 +292,15 @@ int br_min_mtu(const struct net_bridge *br) /* * Recomputes features using slave's features */ -void br_features_recompute(struct net_bridge *br) +u32 br_features_recompute(struct net_bridge *br, u32 features) { struct net_bridge_port *p; - u32 features, mask; + u32 mask; - features = mask = br->feature_mask; if (list_empty(&br->port_list)) - goto done; + return features; + mask = features; features &= ~NETIF_F_ONE_FOR_ALL; list_for_each_entry(p, &br->port_list, list) { @@ -307,8 +308,7 @@ void br_features_recompute(struct net_bridge *br) p->dev->features, mask); } -done: - br->dev->features = netdev_fix_features(br->dev, features); + return features; } /* called with RTNL */ @@ -339,6 +339,8 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) if (IS_ERR(p)) return PTR_ERR(p); + call_netdevice_notifiers(NETDEV_JOIN, dev); + err = dev_set_promiscuity(dev, 1); if (err) goto put_back; @@ -373,9 +375,10 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) list_add_rcu(&p->list, &br->port_list); + netdev_update_features(br->dev); + spin_lock_bh(&br->lock); changed_addr = br_stp_recalculate_bridge_id(br); - br_features_recompute(br); if ((dev->flags & IFF_UP) && netif_carrier_ok(dev) && (br->dev->flags & IFF_UP)) @@ -423,9 +426,10 @@ int br_del_if(struct net_bridge *br, struct net_device *dev) spin_lock_bh(&br->lock); br_stp_recalculate_bridge_id(br); - br_features_recompute(br); spin_unlock_bh(&br->lock); + netdev_update_features(br->dev); + return 0; } diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 785932d7ad3..f3ac1e858ee 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -165,7 +165,7 @@ rx_handler_result_t br_handle_frame(struct sk_buff **pskb) goto drop; /* If STP is turned off, then forward */ - if (p->br->stp_enabled == BR_NO_STP) + if (p->br->stp_enabled == BR_NO_STP && dest[5] == 0) goto forward; if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev, diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 59660c909a7..2f14eafdeea 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -413,7 +413,7 @@ out: #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br, - struct in6_addr *group) + const struct in6_addr *group) { struct sk_buff *skb; struct ipv6hdr *ip6h; @@ -1115,7 +1115,7 @@ static int br_ip4_multicast_query(struct net_bridge *br, struct net_bridge_port *port, struct sk_buff *skb) { - struct iphdr *iph = ip_hdr(skb); + const struct iphdr *iph = ip_hdr(skb); struct igmphdr *ih = igmp_hdr(skb); struct net_bridge_mdb_entry *mp; struct igmpv3_query *ih3; @@ -1190,7 +1190,7 @@ static int br_ip6_multicast_query(struct net_bridge *br, struct net_bridge_port *port, struct sk_buff *skb) { - struct ipv6hdr *ip6h = ipv6_hdr(skb); + const struct ipv6hdr *ip6h = ipv6_hdr(skb); struct mld_msg *mld = (struct mld_msg *) icmp6_hdr(skb); struct net_bridge_mdb_entry *mp; struct mld2_query *mld2q; @@ -1198,7 +1198,7 @@ static int br_ip6_multicast_query(struct net_bridge *br, struct net_bridge_port_group __rcu **pp; unsigned long max_delay; unsigned long now = jiffies; - struct in6_addr *group = NULL; + const struct in6_addr *group = NULL; int err = 0; spin_lock(&br->multicast_lock); @@ -1356,7 +1356,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br, struct sk_buff *skb) { struct sk_buff *skb2 = skb; - struct iphdr *iph; + const struct iphdr *iph; struct igmphdr *ih; unsigned len; unsigned offset; @@ -1452,7 +1452,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br, struct sk_buff *skb) { struct sk_buff *skb2; - struct ipv6hdr *ip6h; + const struct ipv6hdr *ip6h; struct icmp6hdr *icmp6h; u8 nexthdr; unsigned len; diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index f3bc322c589..3fa123185e8 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -117,6 +117,10 @@ static struct dst_ops fake_dst_ops = { * ipt_REJECT needs it. Future netfilter modules might * require us to fill additional fields. */ +static const u32 br_dst_default_metrics[RTAX_MAX] = { + [RTAX_MTU - 1] = 1500, +}; + void br_netfilter_rtable_init(struct net_bridge *br) { struct rtable *rt = &br->fake_rtable; @@ -124,7 +128,7 @@ void br_netfilter_rtable_init(struct net_bridge *br) atomic_set(&rt->dst.__refcnt, 1); rt->dst.dev = br->dev; rt->dst.path = &rt->dst; - dst_metric_set(&rt->dst, RTAX_MTU, 1500); + dst_init_metrics(&rt->dst, br_dst_default_metrics, true); rt->dst.flags = DST_NOXFRM; rt->dst.ops = &fake_dst_ops; } @@ -219,7 +223,7 @@ static inline void nf_bridge_update_protocol(struct sk_buff *skb) static int br_parse_ip_options(struct sk_buff *skb) { struct ip_options *opt; - struct iphdr *iph; + const struct iphdr *iph; struct net_device *dev = skb->dev; u32 len; @@ -554,7 +558,7 @@ static unsigned int br_nf_pre_routing_ipv6(unsigned int hook, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - struct ipv6hdr *hdr; + const struct ipv6hdr *hdr; u32 pkt_len; if (skb->len < sizeof(struct ipv6hdr)) @@ -737,7 +741,7 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff *skb, nf_bridge->mask |= BRNF_PKT_TYPE; } - if (br_parse_ip_options(skb)) + if (pf == PF_INET && br_parse_ip_options(skb)) return NF_DROP; /* The physdev module checks on this */ diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 134a2ff6b98..6814083a92f 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -120,8 +120,9 @@ static int br_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) int idx; idx = 0; - for_each_netdev(net, dev) { - struct net_bridge_port *port = br_port_get_rtnl(dev); + rcu_read_lock(); + for_each_netdev_rcu(net, dev) { + struct net_bridge_port *port = br_port_get_rcu(dev); /* not a bridge port */ if (!port || idx < cb->args[0]) @@ -135,7 +136,7 @@ static int br_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) skip: ++idx; } - + rcu_read_unlock(); cb->args[0] = idx; return skb->len; @@ -217,19 +218,24 @@ int __init br_netlink_init(void) if (err < 0) goto err1; - err = __rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, br_dump_ifinfo); + err = __rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, + br_dump_ifinfo, NULL); if (err) goto err2; - err = __rtnl_register(PF_BRIDGE, RTM_SETLINK, br_rtm_setlink, NULL); + err = __rtnl_register(PF_BRIDGE, RTM_SETLINK, + br_rtm_setlink, NULL, NULL); if (err) goto err3; - err = __rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, br_fdb_add, NULL); + err = __rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, + br_fdb_add, NULL, NULL); if (err) goto err3; - err = __rtnl_register(PF_BRIDGE, RTM_DELNEIGH, br_fdb_delete, NULL); + err = __rtnl_register(PF_BRIDGE, RTM_DELNEIGH, + br_fdb_delete, NULL, NULL); if (err) goto err3; - err = __rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, br_fdb_dump); + err = __rtnl_register(PF_BRIDGE, RTM_GETNEIGH, + NULL, br_fdb_dump, NULL); if (err) goto err3; diff --git a/net/bridge/br_notify.c b/net/bridge/br_notify.c index 606b323e8a0..6545ee9591d 100644 --- a/net/bridge/br_notify.c +++ b/net/bridge/br_notify.c @@ -66,10 +66,7 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v break; case NETDEV_FEAT_CHANGE: - spin_lock_bh(&br->lock); - if (netif_running(br->dev)) - br_features_recompute(br); - spin_unlock_bh(&br->lock); + netdev_update_features(br->dev); break; case NETDEV_DOWN: diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index e2a40343aa0..54578f274d8 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -183,7 +183,6 @@ struct net_bridge struct br_cpu_netstats __percpu *stats; spinlock_t hash_lock; struct hlist_head hash[BR_HASH_SIZE]; - u32 feature_mask; #ifdef CONFIG_BRIDGE_NETFILTER struct rtable fake_rtable; bool nf_call_iptables; @@ -379,7 +378,7 @@ extern int br_add_if(struct net_bridge *br, extern int br_del_if(struct net_bridge *br, struct net_device *dev); extern int br_min_mtu(const struct net_bridge *br); -extern void br_features_recompute(struct net_bridge *br); +extern u32 br_features_recompute(struct net_bridge *br, u32 features); /* br_input.c */ extern int br_handle_frame_finish(struct sk_buff *skb); diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 893669caa8d..2b5ca1a0054 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -1766,7 +1766,7 @@ static int compat_table_info(const struct ebt_table_info *info, newinfo->entries_size = size; - xt_compat_init_offsets(AF_INET, info->nentries); + xt_compat_init_offsets(NFPROTO_BRIDGE, info->nentries); return EBT_ENTRY_ITERATE(entries, size, compat_calc_entry, info, entries, newinfo); } @@ -1882,15 +1882,14 @@ static int compat_mtw_from_user(struct compat_ebt_entry_mwt *mwt, struct xt_match *match; struct xt_target *wt; void *dst = NULL; - int off, pad = 0, ret = 0; - unsigned int size_kern, entry_offset, match_size = mwt->match_size; + int off, pad = 0; + unsigned int size_kern, match_size = mwt->match_size; strlcpy(name, mwt->u.name, sizeof(name)); if (state->buf_kern_start) dst = state->buf_kern_start + state->buf_kern_offset; - entry_offset = (unsigned char *) mwt - base; switch (compat_mwt) { case EBT_COMPAT_MATCH: match = try_then_request_module(xt_find_match(NFPROTO_BRIDGE, @@ -1933,13 +1932,9 @@ static int compat_mtw_from_user(struct compat_ebt_entry_mwt *mwt, size_kern = wt->targetsize; module_put(wt->me); break; - } - if (!dst) { - ret = xt_compat_add_offset(NFPROTO_BRIDGE, entry_offset, - off + ebt_compat_entry_padsize()); - if (ret < 0) - return ret; + default: + return -EINVAL; } state->buf_kern_offset += match_size + off; @@ -2016,50 +2011,6 @@ static int ebt_size_mwt(struct compat_ebt_entry_mwt *match32, return growth; } -#define EBT_COMPAT_WATCHER_ITERATE(e, fn, args...) \ -({ \ - unsigned int __i; \ - int __ret = 0; \ - struct compat_ebt_entry_mwt *__watcher; \ - \ - for (__i = e->watchers_offset; \ - __i < (e)->target_offset; \ - __i += __watcher->watcher_size + \ - sizeof(struct compat_ebt_entry_mwt)) { \ - __watcher = (void *)(e) + __i; \ - __ret = fn(__watcher , ## args); \ - if (__ret != 0) \ - break; \ - } \ - if (__ret == 0) { \ - if (__i != (e)->target_offset) \ - __ret = -EINVAL; \ - } \ - __ret; \ -}) - -#define EBT_COMPAT_MATCH_ITERATE(e, fn, args...) \ -({ \ - unsigned int __i; \ - int __ret = 0; \ - struct compat_ebt_entry_mwt *__match; \ - \ - for (__i = sizeof(struct ebt_entry); \ - __i < (e)->watchers_offset; \ - __i += __match->match_size + \ - sizeof(struct compat_ebt_entry_mwt)) { \ - __match = (void *)(e) + __i; \ - __ret = fn(__match , ## args); \ - if (__ret != 0) \ - break; \ - } \ - if (__ret == 0) { \ - if (__i != (e)->watchers_offset) \ - __ret = -EINVAL; \ - } \ - __ret; \ -}) - /* called for all ebt_entry structures. */ static int size_entry_mwt(struct ebt_entry *entry, const unsigned char *base, unsigned int *total, @@ -2132,6 +2083,14 @@ static int size_entry_mwt(struct ebt_entry *entry, const unsigned char *base, } } + if (state->buf_kern_start == NULL) { + unsigned int offset = buf_start - (char *) base; + + ret = xt_compat_add_offset(NFPROTO_BRIDGE, offset, new_offset); + if (ret < 0) + return ret; + } + startoff = state->buf_user_offset - startoff; BUG_ON(*total < startoff); @@ -2240,6 +2199,7 @@ static int compat_do_replace(struct net *net, void __user *user, xt_compat_lock(NFPROTO_BRIDGE); + xt_compat_init_offsets(NFPROTO_BRIDGE, tmp.nentries); ret = compat_copy_entries(entries_tmp, tmp.entries_size, &state); if (ret < 0) goto out_unlock; diff --git a/net/caif/Makefile b/net/caif/Makefile index 9d38e406e4a..ebcd4e7e6f4 100644 --- a/net/caif/Makefile +++ b/net/caif/Makefile @@ -5,7 +5,7 @@ caif-y := caif_dev.o \ cffrml.o cfveil.o cfdbgl.o\ cfserl.o cfdgml.o \ cfrfml.o cfvidl.o cfutill.o \ - cfsrvl.o cfpkt_skbuff.o caif_config_util.o + cfsrvl.o cfpkt_skbuff.o obj-$(CONFIG_CAIF) += caif.o obj-$(CONFIG_CAIF_NETDEV) += chnl_net.o diff --git a/net/caif/caif_config_util.c b/net/caif/caif_config_util.c deleted file mode 100644 index 9b63e4e3910..00000000000 --- a/net/caif/caif_config_util.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland sjur.brandeland@stericsson.com - * License terms: GNU General Public License (GPL) version 2 - */ - -#include <linux/module.h> -#include <linux/spinlock.h> -#include <net/caif/cfctrl.h> -#include <net/caif/cfcnfg.h> -#include <net/caif/caif_dev.h> - -int caif_connect_req_to_link_param(struct cfcnfg *cnfg, - struct caif_connect_request *s, - struct cfctrl_link_param *l) -{ - struct dev_info *dev_info; - enum cfcnfg_phy_preference pref; - int res; - - memset(l, 0, sizeof(*l)); - /* In caif protocol low value is high priority */ - l->priority = CAIF_PRIO_MAX - s->priority + 1; - - if (s->ifindex != 0){ - res = cfcnfg_get_id_from_ifi(cnfg, s->ifindex); - if (res < 0) - return res; - l->phyid = res; - } - else { - switch (s->link_selector) { - case CAIF_LINK_HIGH_BANDW: - pref = CFPHYPREF_HIGH_BW; - break; - case CAIF_LINK_LOW_LATENCY: - pref = CFPHYPREF_LOW_LAT; - break; - default: - return -EINVAL; - } - dev_info = cfcnfg_get_phyid(cnfg, pref); - if (dev_info == NULL) - return -ENODEV; - l->phyid = dev_info->id; - } - switch (s->protocol) { - case CAIFPROTO_AT: - l->linktype = CFCTRL_SRV_VEI; - if (s->sockaddr.u.at.type == CAIF_ATTYPE_PLAIN) - l->chtype = 0x02; - else - l->chtype = s->sockaddr.u.at.type; - l->endpoint = 0x00; - break; - case CAIFPROTO_DATAGRAM: - l->linktype = CFCTRL_SRV_DATAGRAM; - l->chtype = 0x00; - l->u.datagram.connid = s->sockaddr.u.dgm.connection_id; - break; - case CAIFPROTO_DATAGRAM_LOOP: - l->linktype = CFCTRL_SRV_DATAGRAM; - l->chtype = 0x03; - l->endpoint = 0x00; - l->u.datagram.connid = s->sockaddr.u.dgm.connection_id; - break; - case CAIFPROTO_RFM: - l->linktype = CFCTRL_SRV_RFM; - l->u.datagram.connid = s->sockaddr.u.rfm.connection_id; - strncpy(l->u.rfm.volume, s->sockaddr.u.rfm.volume, - sizeof(l->u.rfm.volume)-1); - l->u.rfm.volume[sizeof(l->u.rfm.volume)-1] = 0; - break; - case CAIFPROTO_UTIL: - l->linktype = CFCTRL_SRV_UTIL; - l->endpoint = 0x00; - l->chtype = 0x00; - strncpy(l->u.utility.name, s->sockaddr.u.util.service, - sizeof(l->u.utility.name)-1); - l->u.utility.name[sizeof(l->u.utility.name)-1] = 0; - caif_assert(sizeof(l->u.utility.name) > 10); - l->u.utility.paramlen = s->param.size; - if (l->u.utility.paramlen > sizeof(l->u.utility.params)) - l->u.utility.paramlen = sizeof(l->u.utility.params); - - memcpy(l->u.utility.params, s->param.data, - l->u.utility.paramlen); - - break; - case CAIFPROTO_DEBUG: - l->linktype = CFCTRL_SRV_DBG; - l->endpoint = s->sockaddr.u.dbg.service; - l->chtype = s->sockaddr.u.dbg.type; - break; - default: - return -EINVAL; - } - return 0; -} diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c index 75e00d59eb4..682c0fedf36 100644 --- a/net/caif/caif_dev.c +++ b/net/caif/caif_dev.c @@ -12,49 +12,51 @@ #define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ #include <linux/version.h> -#include <linux/module.h> #include <linux/kernel.h> #include <linux/if_arp.h> #include <linux/net.h> #include <linux/netdevice.h> -#include <linux/skbuff.h> -#include <linux/sched.h> -#include <linux/wait.h> +#include <linux/mutex.h> #include <net/netns/generic.h> #include <net/net_namespace.h> #include <net/pkt_sched.h> #include <net/caif/caif_device.h> -#include <net/caif/caif_dev.h> #include <net/caif/caif_layer.h> #include <net/caif/cfpkt.h> #include <net/caif/cfcnfg.h> MODULE_LICENSE("GPL"); -#define TIMEOUT (HZ*5) /* Used for local tracking of the CAIF net devices */ struct caif_device_entry { struct cflayer layer; struct list_head list; - atomic_t in_use; - atomic_t state; - u16 phyid; struct net_device *netdev; - wait_queue_head_t event; + int __percpu *pcpu_refcnt; }; struct caif_device_entry_list { struct list_head list; /* Protects simulanous deletes in list */ - spinlock_t lock; + struct mutex lock; }; struct caif_net { + struct cfcnfg *cfg; struct caif_device_entry_list caifdevs; }; static int caif_net_id; -static struct cfcnfg *cfg; + +struct cfcnfg *get_cfcnfg(struct net *net) +{ + struct caif_net *caifn; + BUG_ON(!net); + caifn = net_generic(net, caif_net_id); + BUG_ON(!caifn); + return caifn->cfg; +} +EXPORT_SYMBOL(get_cfcnfg); static struct caif_device_entry_list *caif_device_list(struct net *net) { @@ -65,19 +67,39 @@ static struct caif_device_entry_list *caif_device_list(struct net *net) return &caifn->caifdevs; } +static void caifd_put(struct caif_device_entry *e) +{ + irqsafe_cpu_dec(*e->pcpu_refcnt); +} + +static void caifd_hold(struct caif_device_entry *e) +{ + irqsafe_cpu_inc(*e->pcpu_refcnt); +} + +static int caifd_refcnt_read(struct caif_device_entry *e) +{ + int i, refcnt = 0; + for_each_possible_cpu(i) + refcnt += *per_cpu_ptr(e->pcpu_refcnt, i); + return refcnt; +} + /* Allocate new CAIF device. */ static struct caif_device_entry *caif_device_alloc(struct net_device *dev) { struct caif_device_entry_list *caifdevs; struct caif_device_entry *caifd; + caifdevs = caif_device_list(dev_net(dev)); BUG_ON(!caifdevs); + caifd = kzalloc(sizeof(*caifd), GFP_ATOMIC); if (!caifd) return NULL; + caifd->pcpu_refcnt = alloc_percpu(int); caifd->netdev = dev; - list_add(&caifd->list, &caifdevs->list); - init_waitqueue_head(&caifd->event); + dev_hold(dev); return caifd; } @@ -87,37 +109,16 @@ static struct caif_device_entry *caif_get(struct net_device *dev) caif_device_list(dev_net(dev)); struct caif_device_entry *caifd; BUG_ON(!caifdevs); - list_for_each_entry(caifd, &caifdevs->list, list) { + list_for_each_entry_rcu(caifd, &caifdevs->list, list) { if (caifd->netdev == dev) return caifd; } return NULL; } -static void caif_device_destroy(struct net_device *dev) -{ - struct caif_device_entry_list *caifdevs = - caif_device_list(dev_net(dev)); - struct caif_device_entry *caifd; - ASSERT_RTNL(); - if (dev->type != ARPHRD_CAIF) - return; - - spin_lock_bh(&caifdevs->lock); - caifd = caif_get(dev); - if (caifd == NULL) { - spin_unlock_bh(&caifdevs->lock); - return; - } - - list_del(&caifd->list); - spin_unlock_bh(&caifdevs->lock); - - kfree(caifd); -} - static int transmit(struct cflayer *layer, struct cfpkt *pkt) { + int err; struct caif_device_entry *caifd = container_of(layer, struct caif_device_entry, layer); struct sk_buff *skb; @@ -125,28 +126,15 @@ static int transmit(struct cflayer *layer, struct cfpkt *pkt) skb = cfpkt_tonative(pkt); skb->dev = caifd->netdev; - dev_queue_xmit(skb); + err = dev_queue_xmit(skb); + if (err > 0) + err = -EIO; - return 0; -} - -static int modemcmd(struct cflayer *layr, enum caif_modemcmd ctrl) -{ - struct caif_device_entry *caifd; - caifd = container_of(layr, struct caif_device_entry, layer); - if (ctrl == _CAIF_MODEMCMD_PHYIF_USEFULL) { - atomic_set(&caifd->in_use, 1); - wake_up_interruptible(&caifd->event); - - } else if (ctrl == _CAIF_MODEMCMD_PHYIF_USELESS) { - atomic_set(&caifd->in_use, 0); - wake_up_interruptible(&caifd->event); - } - return 0; + return err; } /* - * Stuff received packets to associated sockets. + * Stuff received packets into the CAIF stack. * On error, returns non-zero and releases the skb. */ static int receive(struct sk_buff *skb, struct net_device *dev, @@ -154,14 +142,32 @@ static int receive(struct sk_buff *skb, struct net_device *dev, { struct cfpkt *pkt; struct caif_device_entry *caifd; + int err; + pkt = cfpkt_fromnative(CAIF_DIR_IN, skb); + + rcu_read_lock(); caifd = caif_get(dev); - if (!caifd || !caifd->layer.up || !caifd->layer.up->receive) - return NET_RX_DROP; - if (caifd->layer.up->receive(caifd->layer.up, pkt)) + if (!caifd || !caifd->layer.up || !caifd->layer.up->receive || + !netif_oper_up(caifd->netdev)) { + rcu_read_unlock(); + kfree_skb(skb); return NET_RX_DROP; + } + + /* Hold reference to netdevice while using CAIF stack */ + caifd_hold(caifd); + rcu_read_unlock(); + err = caifd->layer.up->receive(caifd->layer.up, pkt); + + /* For -EILSEQ the packet is not freed so so it now */ + if (err == -EILSEQ) + cfpkt_destroy(pkt); + + /* Release reference to stack upwards */ + caifd_put(caifd); return 0; } @@ -172,15 +178,25 @@ static struct packet_type caif_packet_type __read_mostly = { static void dev_flowctrl(struct net_device *dev, int on) { - struct caif_device_entry *caifd = caif_get(dev); - if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) + struct caif_device_entry *caifd; + + rcu_read_lock(); + + caifd = caif_get(dev); + if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) { + rcu_read_unlock(); return; + } + + caifd_hold(caifd); + rcu_read_unlock(); caifd->layer.up->ctrlcmd(caifd->layer.up, on ? _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND : _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND, caifd->layer.id); + caifd_put(caifd); } /* notify Caif of device events */ @@ -192,34 +208,27 @@ static int caif_device_notify(struct notifier_block *me, unsigned long what, struct caif_dev_common *caifdev; enum cfcnfg_phy_preference pref; enum cfcnfg_phy_type phy_type; + struct cfcnfg *cfg; + struct caif_device_entry_list *caifdevs = + caif_device_list(dev_net(dev)); if (dev->type != ARPHRD_CAIF) return 0; + cfg = get_cfcnfg(dev_net(dev)); + if (cfg == NULL) + return 0; + switch (what) { case NETDEV_REGISTER: - netdev_info(dev, "register\n"); caifd = caif_device_alloc(dev); - if (caifd == NULL) - break; + if (!caifd) + return 0; + caifdev = netdev_priv(dev); caifdev->flowctrl = dev_flowctrl; - atomic_set(&caifd->state, what); - break; - case NETDEV_UP: - netdev_info(dev, "up\n"); - caifd = caif_get(dev); - if (caifd == NULL) - break; - caifdev = netdev_priv(dev); - if (atomic_read(&caifd->state) == NETDEV_UP) { - netdev_info(dev, "already up\n"); - break; - } - atomic_set(&caifd->state, what); caifd->layer.transmit = transmit; - caifd->layer.modemcmd = modemcmd; if (caifdev->use_frag) phy_type = CFPHYTYPE_FRAG; @@ -237,62 +246,94 @@ static int caif_device_notify(struct notifier_block *me, unsigned long what, pref = CFPHYPREF_HIGH_BW; break; } - dev_hold(dev); + strncpy(caifd->layer.name, dev->name, + sizeof(caifd->layer.name) - 1); + caifd->layer.name[sizeof(caifd->layer.name) - 1] = 0; + + mutex_lock(&caifdevs->lock); + list_add_rcu(&caifd->list, &caifdevs->list); + cfcnfg_add_phy_layer(cfg, phy_type, dev, &caifd->layer, - &caifd->phyid, pref, caifdev->use_fcs, caifdev->use_stx); - strncpy(caifd->layer.name, dev->name, - sizeof(caifd->layer.name) - 1); - caifd->layer.name[sizeof(caifd->layer.name) - 1] = 0; + mutex_unlock(&caifdevs->lock); break; - case NETDEV_GOING_DOWN: + case NETDEV_UP: + rcu_read_lock(); + caifd = caif_get(dev); - if (caifd == NULL) + if (caifd == NULL) { + rcu_read_unlock(); break; - netdev_info(dev, "going down\n"); + } - if (atomic_read(&caifd->state) == NETDEV_GOING_DOWN || - atomic_read(&caifd->state) == NETDEV_DOWN) - break; + cfcnfg_set_phy_state(cfg, &caifd->layer, true); + rcu_read_unlock(); - atomic_set(&caifd->state, what); - if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) - return -EINVAL; - caifd->layer.up->ctrlcmd(caifd->layer.up, - _CAIF_CTRLCMD_PHYIF_DOWN_IND, - caifd->layer.id); - might_sleep(); - wait_event_interruptible_timeout(caifd->event, - atomic_read(&caifd->in_use) == 0, - TIMEOUT); break; case NETDEV_DOWN: + rcu_read_lock(); + caifd = caif_get(dev); - if (caifd == NULL) - break; - netdev_info(dev, "down\n"); - if (atomic_read(&caifd->in_use)) - netdev_warn(dev, - "Unregistering an active CAIF device\n"); - cfcnfg_del_phy_layer(cfg, &caifd->layer); - dev_put(dev); - atomic_set(&caifd->state, what); + if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) { + rcu_read_unlock(); + return -EINVAL; + } + + cfcnfg_set_phy_state(cfg, &caifd->layer, false); + caifd_hold(caifd); + rcu_read_unlock(); + + caifd->layer.up->ctrlcmd(caifd->layer.up, + _CAIF_CTRLCMD_PHYIF_DOWN_IND, + caifd->layer.id); + caifd_put(caifd); break; case NETDEV_UNREGISTER: + mutex_lock(&caifdevs->lock); + caifd = caif_get(dev); - if (caifd == NULL) + if (caifd == NULL) { + mutex_unlock(&caifdevs->lock); + break; + } + list_del_rcu(&caifd->list); + + /* + * NETDEV_UNREGISTER is called repeatedly until all reference + * counts for the net-device are released. If references to + * caifd is taken, simply ignore NETDEV_UNREGISTER and wait for + * the next call to NETDEV_UNREGISTER. + * + * If any packets are in flight down the CAIF Stack, + * cfcnfg_del_phy_layer will return nonzero. + * If no packets are in flight, the CAIF Stack associated + * with the net-device un-registering is freed. + */ + + if (caifd_refcnt_read(caifd) != 0 || + cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0) { + + pr_info("Wait for device inuse\n"); + /* Enrole device if CAIF Stack is still in use */ + list_add_rcu(&caifd->list, &caifdevs->list); + mutex_unlock(&caifdevs->lock); break; - netdev_info(dev, "unregister\n"); - atomic_set(&caifd->state, what); - caif_device_destroy(dev); + } + + synchronize_rcu(); + dev_put(caifd->netdev); + free_percpu(caifd->pcpu_refcnt); + kfree(caifd); + + mutex_unlock(&caifdevs->lock); break; } return 0; @@ -303,48 +344,60 @@ static struct notifier_block caif_device_notifier = { .priority = 0, }; -int caif_connect_client(struct caif_connect_request *conn_req, - struct cflayer *client_layer, int *ifindex, - int *headroom, int *tailroom) -{ - struct cfctrl_link_param param; - int ret; - - ret = caif_connect_req_to_link_param(cfg, conn_req, ¶m); - if (ret) - return ret; - /* Hook up the adaptation layer. */ - return cfcnfg_add_adaptation_layer(cfg, ¶m, - client_layer, ifindex, - headroom, tailroom); -} -EXPORT_SYMBOL(caif_connect_client); - -int caif_disconnect_client(struct cflayer *adap_layer) -{ - return cfcnfg_disconn_adapt_layer(cfg, adap_layer); -} -EXPORT_SYMBOL(caif_disconnect_client); - /* Per-namespace Caif devices handling */ static int caif_init_net(struct net *net) { struct caif_net *caifn = net_generic(net, caif_net_id); + BUG_ON(!caifn); INIT_LIST_HEAD(&caifn->caifdevs.list); - spin_lock_init(&caifn->caifdevs.lock); + mutex_init(&caifn->caifdevs.lock); + + caifn->cfg = cfcnfg_create(); + if (!caifn->cfg) { + pr_warn("can't create cfcnfg\n"); + return -ENOMEM; + } + return 0; } static void caif_exit_net(struct net *net) { - struct net_device *dev; + struct caif_device_entry *caifd, *tmp; + struct caif_device_entry_list *caifdevs = + caif_device_list(net); + struct cfcnfg *cfg; + rtnl_lock(); - for_each_netdev(net, dev) { - if (dev->type != ARPHRD_CAIF) - continue; - dev_close(dev); - caif_device_destroy(dev); + mutex_lock(&caifdevs->lock); + + cfg = get_cfcnfg(net); + if (cfg == NULL) { + mutex_unlock(&caifdevs->lock); + return; + } + + list_for_each_entry_safe(caifd, tmp, &caifdevs->list, list) { + int i = 0; + list_del_rcu(&caifd->list); + cfcnfg_set_phy_state(cfg, &caifd->layer, false); + + while (i < 10 && + (caifd_refcnt_read(caifd) != 0 || + cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0)) { + + pr_info("Wait for device inuse\n"); + msleep(250); + i++; + } + synchronize_rcu(); + dev_put(caifd->netdev); + free_percpu(caifd->pcpu_refcnt); + kfree(caifd); } + cfcnfg_remove(cfg); + + mutex_unlock(&caifdevs->lock); rtnl_unlock(); } @@ -359,32 +412,23 @@ static struct pernet_operations caif_net_ops = { static int __init caif_device_init(void) { int result; - cfg = cfcnfg_create(); - if (!cfg) { - pr_warn("can't create cfcnfg\n"); - goto err_cfcnfg_create_failed; - } + result = register_pernet_device(&caif_net_ops); - if (result) { - kfree(cfg); - cfg = NULL; + if (result) return result; - } - dev_add_pack(&caif_packet_type); + register_netdevice_notifier(&caif_device_notifier); + dev_add_pack(&caif_packet_type); return result; -err_cfcnfg_create_failed: - return -ENODEV; } static void __exit caif_device_exit(void) { - dev_remove_pack(&caif_packet_type); unregister_pernet_device(&caif_net_ops); unregister_netdevice_notifier(&caif_device_notifier); - cfcnfg_remove(cfg); + dev_remove_pack(&caif_packet_type); } module_init(caif_device_init); diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c index 20212424e2e..a9862808645 100644 --- a/net/caif/caif_socket.c +++ b/net/caif/caif_socket.c @@ -19,7 +19,7 @@ #include <linux/uaccess.h> #include <linux/debugfs.h> #include <linux/caif/caif_socket.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include <net/sock.h> #include <net/tcp_states.h> #include <net/caif/caif_layer.h> @@ -48,6 +48,7 @@ static struct dentry *debugfsdir; #ifdef CONFIG_DEBUG_FS struct debug_fs_counter { atomic_t caif_nr_socks; + atomic_t caif_sock_create; atomic_t num_connect_req; atomic_t num_connect_resp; atomic_t num_connect_fail_resp; @@ -59,11 +60,11 @@ struct debug_fs_counter { atomic_t num_rx_flow_on; }; static struct debug_fs_counter cnt; -#define dbfs_atomic_inc(v) atomic_inc(v) -#define dbfs_atomic_dec(v) atomic_dec(v) +#define dbfs_atomic_inc(v) atomic_inc_return(v) +#define dbfs_atomic_dec(v) atomic_dec_return(v) #else -#define dbfs_atomic_inc(v) -#define dbfs_atomic_dec(v) +#define dbfs_atomic_inc(v) 0 +#define dbfs_atomic_dec(v) 0 #endif struct caifsock { @@ -155,9 +156,10 @@ static int caif_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >= (unsigned)sk->sk_rcvbuf && rx_flow_is_on(cf_sk)) { - pr_debug("sending flow OFF (queue len = %d %d)\n", - atomic_read(&cf_sk->sk.sk_rmem_alloc), - sk_rcvbuf_lowwater(cf_sk)); + if (net_ratelimit()) + pr_debug("sending flow OFF (queue len = %d %d)\n", + atomic_read(&cf_sk->sk.sk_rmem_alloc), + sk_rcvbuf_lowwater(cf_sk)); set_rx_flow_off(cf_sk); dbfs_atomic_inc(&cnt.num_rx_flow_off); caif_flow_ctrl(sk, CAIF_MODEMCMD_FLOW_OFF_REQ); @@ -168,7 +170,8 @@ static int caif_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) return err; if (!sk_rmem_schedule(sk, skb->truesize) && rx_flow_is_on(cf_sk)) { set_rx_flow_off(cf_sk); - pr_debug("sending flow OFF due to rmem_schedule\n"); + if (net_ratelimit()) + pr_debug("sending flow OFF due to rmem_schedule\n"); dbfs_atomic_inc(&cnt.num_rx_flow_off); caif_flow_ctrl(sk, CAIF_MODEMCMD_FLOW_OFF_REQ); } @@ -202,13 +205,25 @@ static int caif_sktrecv_cb(struct cflayer *layr, struct cfpkt *pkt) skb = cfpkt_tonative(pkt); if (unlikely(cf_sk->sk.sk_state != CAIF_CONNECTED)) { - cfpkt_destroy(pkt); + kfree_skb(skb); return 0; } caif_queue_rcv_skb(&cf_sk->sk, skb); return 0; } +static void cfsk_hold(struct cflayer *layr) +{ + struct caifsock *cf_sk = container_of(layr, struct caifsock, layer); + sock_hold(&cf_sk->sk); +} + +static void cfsk_put(struct cflayer *layr) +{ + struct caifsock *cf_sk = container_of(layr, struct caifsock, layer); + sock_put(&cf_sk->sk); +} + /* Packet Control Callback function called from CAIF */ static void caif_ctrl_cb(struct cflayer *layr, enum caif_ctrlcmd flow, @@ -232,6 +247,8 @@ static void caif_ctrl_cb(struct cflayer *layr, case CAIF_CTRLCMD_INIT_RSP: /* We're now connected */ + caif_client_register_refcnt(&cf_sk->layer, + cfsk_hold, cfsk_put); dbfs_atomic_inc(&cnt.num_connect_resp); cf_sk->sk.sk_state = CAIF_CONNECTED; set_tx_flow_on(cf_sk); @@ -242,7 +259,6 @@ static void caif_ctrl_cb(struct cflayer *layr, /* We're now disconnected */ cf_sk->sk.sk_state = CAIF_DISCONNECTED; cf_sk->sk.sk_state_change(&cf_sk->sk); - cfcnfg_release_adap_layer(&cf_sk->layer); break; case CAIF_CTRLCMD_INIT_FAIL_RSP: @@ -521,7 +537,7 @@ static int transmit_skb(struct sk_buff *skb, struct caifsock *cf_sk, struct cfpkt *pkt; pkt = cfpkt_fromnative(CAIF_DIR_OUT, skb); - memset(cfpkt_info(pkt), 0, sizeof(struct caif_payload_info)); + memset(skb->cb, 0, sizeof(struct caif_payload_info)); if (cf_sk->layer.dn == NULL) return -EINVAL; @@ -591,7 +607,9 @@ static int caif_seqpkt_sendmsg(struct kiocb *kiocb, struct socket *sock, goto err; ret = transmit_skb(skb, cf_sk, noblock, timeo); if (ret < 0) - goto err; + /* skb is already freed */ + return ret; + return len; err: kfree_skb(skb); @@ -797,7 +815,8 @@ static int caif_connect(struct socket *sock, struct sockaddr *uaddr, sk->sk_state == CAIF_DISCONNECTED); if (sk->sk_shutdown & SHUTDOWN_MASK) { /* Allow re-connect after SHUTDOWN_IND */ - caif_disconnect_client(&cf_sk->layer); + caif_disconnect_client(sock_net(sk), &cf_sk->layer); + caif_free_client(&cf_sk->layer); break; } /* No reconnect on a seqpacket socket */ @@ -823,7 +842,7 @@ static int caif_connect(struct socket *sock, struct sockaddr *uaddr, sock->state = SS_CONNECTING; sk->sk_state = CAIF_CONNECTING; - /* Check priority value coming from socket */ + /* Check priority value comming from socket */ /* if priority value is out of range it will be ajusted */ if (cf_sk->sk.sk_priority > CAIF_PRIO_MAX) cf_sk->conn_req.priority = CAIF_PRIO_MAX; @@ -837,8 +856,10 @@ static int caif_connect(struct socket *sock, struct sockaddr *uaddr, dbfs_atomic_inc(&cnt.num_connect_req); cf_sk->layer.receive = caif_sktrecv_cb; - err = caif_connect_client(&cf_sk->conn_req, + + err = caif_connect_client(sock_net(sk), &cf_sk->conn_req, &cf_sk->layer, &ifindex, &headroom, &tailroom); + if (err < 0) { cf_sk->sk.sk_socket->state = SS_UNCONNECTED; cf_sk->sk.sk_state = CAIF_DISCONNECTED; @@ -906,7 +927,6 @@ static int caif_release(struct socket *sock) { struct sock *sk = sock->sk; struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - int res = 0; if (!sk) return 0; @@ -918,13 +938,14 @@ static int caif_release(struct socket *sock) * caif_queue_rcv_skb checks SOCK_DEAD holding the queue lock, * this ensures no packets when sock is dead. */ - spin_lock(&sk->sk_receive_queue.lock); + spin_lock_bh(&sk->sk_receive_queue.lock); sock_set_flag(sk, SOCK_DEAD); - spin_unlock(&sk->sk_receive_queue.lock); + spin_unlock_bh(&sk->sk_receive_queue.lock); sock->sk = NULL; dbfs_atomic_inc(&cnt.num_disconnect); + WARN_ON(IS_ERR(cf_sk->debugfs_socket_dir)); if (cf_sk->debugfs_socket_dir != NULL) debugfs_remove_recursive(cf_sk->debugfs_socket_dir); @@ -932,19 +953,15 @@ static int caif_release(struct socket *sock) sk->sk_state = CAIF_DISCONNECTED; sk->sk_shutdown = SHUTDOWN_MASK; - if (cf_sk->sk.sk_socket->state == SS_CONNECTED || - cf_sk->sk.sk_socket->state == SS_CONNECTING) - res = caif_disconnect_client(&cf_sk->layer); - + caif_disconnect_client(sock_net(sk), &cf_sk->layer); cf_sk->sk.sk_socket->state = SS_DISCONNECTING; wake_up_interruptible_poll(sk_sleep(sk), POLLERR|POLLHUP); sock_orphan(sk); - cf_sk->layer.dn = NULL; sk_stream_kill_queues(&cf_sk->sk); release_sock(sk); sock_put(sk); - return res; + return 0; } /* Copied from af_unix.c:unix_poll(), added CAIF tx_flow handling */ @@ -1031,16 +1048,18 @@ static void caif_sock_destructor(struct sock *sk) caif_assert(sk_unhashed(sk)); caif_assert(!sk->sk_socket); if (!sock_flag(sk, SOCK_DEAD)) { - pr_info("Attempt to release alive CAIF socket: %p\n", sk); + pr_debug("Attempt to release alive CAIF socket: %p\n", sk); return; } sk_stream_kill_queues(&cf_sk->sk); dbfs_atomic_dec(&cnt.caif_nr_socks); + caif_free_client(&cf_sk->layer); } static int caif_create(struct net *net, struct socket *sock, int protocol, int kern) { + int num; struct sock *sk = NULL; struct caifsock *cf_sk = NULL; static struct proto prot = {.name = "PF_CAIF", @@ -1098,19 +1117,21 @@ static int caif_create(struct net *net, struct socket *sock, int protocol, set_rx_flow_on(cf_sk); /* Set default options on configuration */ - cf_sk->sk.sk_priority= CAIF_PRIO_NORMAL; + cf_sk->sk.sk_priority = CAIF_PRIO_NORMAL; cf_sk->conn_req.link_selector = CAIF_LINK_LOW_LATENCY; cf_sk->conn_req.protocol = protocol; /* Increase the number of sockets created. */ dbfs_atomic_inc(&cnt.caif_nr_socks); + num = dbfs_atomic_inc(&cnt.caif_sock_create); #ifdef CONFIG_DEBUG_FS if (!IS_ERR(debugfsdir)) { + /* Fill in some information concerning the misc socket. */ - snprintf(cf_sk->name, sizeof(cf_sk->name), "cfsk%d", - atomic_read(&cnt.caif_nr_socks)); + snprintf(cf_sk->name, sizeof(cf_sk->name), "cfsk%d", num); cf_sk->debugfs_socket_dir = debugfs_create_dir(cf_sk->name, debugfsdir); + debugfs_create_u32("sk_state", S_IRUSR | S_IWUSR, cf_sk->debugfs_socket_dir, (u32 *) &cf_sk->sk.sk_state); @@ -1154,6 +1175,9 @@ static int __init caif_sktinit_module(void) debugfs_create_u32("num_sockets", S_IRUSR | S_IWUSR, debugfsdir, (u32 *) &cnt.caif_nr_socks); + debugfs_create_u32("num_create", S_IRUSR | S_IWUSR, + debugfsdir, + (u32 *) &cnt.caif_sock_create); debugfs_create_u32("num_connect_req", S_IRUSR | S_IWUSR, debugfsdir, (u32 *) &cnt.num_connect_req); diff --git a/net/caif/cfcnfg.c b/net/caif/cfcnfg.c index 25c0b198e28..52fe33bee02 100644 --- a/net/caif/cfcnfg.c +++ b/net/caif/cfcnfg.c @@ -10,6 +10,7 @@ #include <linux/stddef.h> #include <linux/slab.h> #include <linux/netdevice.h> +#include <linux/module.h> #include <net/caif/caif_layer.h> #include <net/caif/cfpkt.h> #include <net/caif/cfcnfg.h> @@ -18,11 +19,7 @@ #include <net/caif/cffrml.h> #include <net/caif/cfserl.h> #include <net/caif/cfsrvl.h> - -#include <linux/module.h> -#include <asm/atomic.h> - -#define MAX_PHY_LAYERS 7 +#include <net/caif/caif_dev.h> #define container_obj(layr) container_of(layr, struct cfcnfg, layer) @@ -30,6 +27,9 @@ * to manage physical interfaces */ struct cfcnfg_phyinfo { + struct list_head node; + bool up; + /* Pointer to the layer below the MUX (framing layer) */ struct cflayer *frm_layer; /* Pointer to the lowest actual physical layer */ @@ -39,9 +39,6 @@ struct cfcnfg_phyinfo { /* Preference of the physical in interface */ enum cfcnfg_phy_preference pref; - /* Reference count, number of channels using the device */ - int phy_ref_count; - /* Information about the physical device */ struct dev_info dev_info; @@ -59,8 +56,8 @@ struct cfcnfg { struct cflayer layer; struct cflayer *ctrl; struct cflayer *mux; - u8 last_phyid; - struct cfcnfg_phyinfo phy_layers[MAX_PHY_LAYERS]; + struct list_head phys; + struct mutex lock; }; static void cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, @@ -76,6 +73,9 @@ struct cfcnfg *cfcnfg_create(void) { struct cfcnfg *this; struct cfctrl_rsp *resp; + + might_sleep(); + /* Initiate this layer */ this = kzalloc(sizeof(struct cfcnfg), GFP_ATOMIC); if (!this) { @@ -99,27 +99,33 @@ struct cfcnfg *cfcnfg_create(void) resp->radioset_rsp = cfctrl_resp_func; resp->linksetup_rsp = cfcnfg_linkup_rsp; resp->reject_rsp = cfcnfg_reject_rsp; - - this->last_phyid = 1; + INIT_LIST_HEAD(&this->phys); cfmuxl_set_uplayer(this->mux, this->ctrl, 0); layer_set_dn(this->ctrl, this->mux); layer_set_up(this->ctrl, this); + mutex_init(&this->lock); + return this; out_of_mem: pr_warn("Out of memory\n"); + + synchronize_rcu(); + kfree(this->mux); kfree(this->ctrl); kfree(this); return NULL; } -EXPORT_SYMBOL(cfcnfg_create); void cfcnfg_remove(struct cfcnfg *cfg) { + might_sleep(); if (cfg) { + synchronize_rcu(); + kfree(cfg->mux); - kfree(cfg->ctrl); + cfctrl_remove(cfg->ctrl); kfree(cfg); } } @@ -128,126 +134,77 @@ static void cfctrl_resp_func(void) { } +static struct cfcnfg_phyinfo *cfcnfg_get_phyinfo_rcu(struct cfcnfg *cnfg, + u8 phyid) +{ + struct cfcnfg_phyinfo *phy; + + list_for_each_entry_rcu(phy, &cnfg->phys, node) + if (phy->id == phyid) + return phy; + return NULL; +} + static void cfctrl_enum_resp(void) { } -struct dev_info *cfcnfg_get_phyid(struct cfcnfg *cnfg, +static struct dev_info *cfcnfg_get_phyid(struct cfcnfg *cnfg, enum cfcnfg_phy_preference phy_pref) { - u16 i; - /* Try to match with specified preference */ - for (i = 1; i < MAX_PHY_LAYERS; i++) { - if (cnfg->phy_layers[i].id == i && - cnfg->phy_layers[i].pref == phy_pref && - cnfg->phy_layers[i].frm_layer != NULL) { - caif_assert(cnfg->phy_layers != NULL); - caif_assert(cnfg->phy_layers[i].id == i); - return &cnfg->phy_layers[i].dev_info; - } + struct cfcnfg_phyinfo *phy; + + list_for_each_entry_rcu(phy, &cnfg->phys, node) { + if (phy->up && phy->pref == phy_pref && + phy->frm_layer != NULL) + + return &phy->dev_info; } + /* Otherwise just return something */ - for (i = 1; i < MAX_PHY_LAYERS; i++) { - if (cnfg->phy_layers[i].id == i) { - caif_assert(cnfg->phy_layers != NULL); - caif_assert(cnfg->phy_layers[i].id == i); - return &cnfg->phy_layers[i].dev_info; - } - } + list_for_each_entry_rcu(phy, &cnfg->phys, node) + if (phy->up) + return &phy->dev_info; return NULL; } -static struct cfcnfg_phyinfo *cfcnfg_get_phyinfo(struct cfcnfg *cnfg, - u8 phyid) +static int cfcnfg_get_id_from_ifi(struct cfcnfg *cnfg, int ifi) { - int i; - /* Try to match with specified preference */ - for (i = 0; i < MAX_PHY_LAYERS; i++) - if (cnfg->phy_layers[i].frm_layer != NULL && - cnfg->phy_layers[i].id == phyid) - return &cnfg->phy_layers[i]; - return NULL; -} + struct cfcnfg_phyinfo *phy; - -int cfcnfg_get_id_from_ifi(struct cfcnfg *cnfg, int ifi) -{ - int i; - for (i = 0; i < MAX_PHY_LAYERS; i++) - if (cnfg->phy_layers[i].frm_layer != NULL && - cnfg->phy_layers[i].ifindex == ifi) - return i; + list_for_each_entry_rcu(phy, &cnfg->phys, node) + if (phy->ifindex == ifi && phy->up) + return phy->id; return -ENODEV; } -int cfcnfg_disconn_adapt_layer(struct cfcnfg *cnfg, struct cflayer *adap_layer) +int caif_disconnect_client(struct net *net, struct cflayer *adap_layer) { - u8 channel_id = 0; - int ret = 0; - struct cflayer *servl = NULL; - struct cfcnfg_phyinfo *phyinfo = NULL; - u8 phyid = 0; + u8 channel_id; + struct cfcnfg *cfg = get_cfcnfg(net); caif_assert(adap_layer != NULL); + cfctrl_cancel_req(cfg->ctrl, adap_layer); channel_id = adap_layer->id; - if (adap_layer->dn == NULL || channel_id == 0) { - pr_err("adap_layer->dn == NULL or adap_layer->id is 0\n"); - ret = -ENOTCONN; - goto end; - } - servl = cfmuxl_remove_uplayer(cnfg->mux, channel_id); - if (servl == NULL) { - pr_err("PROTOCOL ERROR - Error removing service_layer Channel_Id(%d)", - channel_id); - ret = -EINVAL; - goto end; - } - layer_set_up(servl, NULL); - ret = cfctrl_linkdown_req(cnfg->ctrl, channel_id, adap_layer); - if (ret) - goto end; - caif_assert(channel_id == servl->id); - if (adap_layer->dn != NULL) { - phyid = cfsrvl_getphyid(adap_layer->dn); - - phyinfo = cfcnfg_get_phyinfo(cnfg, phyid); - if (phyinfo == NULL) { - pr_warn("No interface to send disconnect to\n"); - ret = -ENODEV; - goto end; - } - if (phyinfo->id != phyid || - phyinfo->phy_layer->id != phyid || - phyinfo->frm_layer->id != phyid) { - pr_err("Inconsistency in phy registration\n"); - ret = -EINVAL; - goto end; - } - } - if (phyinfo != NULL && --phyinfo->phy_ref_count == 0 && - phyinfo->phy_layer != NULL && - phyinfo->phy_layer->modemcmd != NULL) { - phyinfo->phy_layer->modemcmd(phyinfo->phy_layer, - _CAIF_MODEMCMD_PHYIF_USELESS); - } -end: - cfsrvl_put(servl); - cfctrl_cancel_req(cnfg->ctrl, adap_layer); + if (channel_id != 0) { + struct cflayer *servl; + servl = cfmuxl_remove_uplayer(cfg->mux, channel_id); + if (servl != NULL) + layer_set_up(servl, NULL); + } else + pr_debug("nothing to disconnect\n"); + cfctrl_linkdown_req(cfg->ctrl, channel_id, adap_layer); + + /* Do RCU sync before initiating cleanup */ + synchronize_rcu(); if (adap_layer->ctrlcmd != NULL) adap_layer->ctrlcmd(adap_layer, CAIF_CTRLCMD_DEINIT_RSP, 0); - return ret; - -} -EXPORT_SYMBOL(cfcnfg_disconn_adapt_layer); + return 0; -void cfcnfg_release_adap_layer(struct cflayer *adap_layer) -{ - if (adap_layer->dn) - cfsrvl_put(adap_layer->dn); } -EXPORT_SYMBOL(cfcnfg_release_adap_layer); +EXPORT_SYMBOL(caif_disconnect_client); static void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id) { @@ -261,49 +218,157 @@ static const int protohead[CFCTRL_SRV_MASK] = { [CFCTRL_SRV_DBG] = 3, }; -int cfcnfg_add_adaptation_layer(struct cfcnfg *cnfg, - struct cfctrl_link_param *param, - struct cflayer *adap_layer, - int *ifindex, + +static int caif_connect_req_to_link_param(struct cfcnfg *cnfg, + struct caif_connect_request *s, + struct cfctrl_link_param *l) +{ + struct dev_info *dev_info; + enum cfcnfg_phy_preference pref; + int res; + + memset(l, 0, sizeof(*l)); + /* In caif protocol low value is high priority */ + l->priority = CAIF_PRIO_MAX - s->priority + 1; + + if (s->ifindex != 0) { + res = cfcnfg_get_id_from_ifi(cnfg, s->ifindex); + if (res < 0) + return res; + l->phyid = res; + } else { + switch (s->link_selector) { + case CAIF_LINK_HIGH_BANDW: + pref = CFPHYPREF_HIGH_BW; + break; + case CAIF_LINK_LOW_LATENCY: + pref = CFPHYPREF_LOW_LAT; + break; + default: + return -EINVAL; + } + dev_info = cfcnfg_get_phyid(cnfg, pref); + if (dev_info == NULL) + return -ENODEV; + l->phyid = dev_info->id; + } + switch (s->protocol) { + case CAIFPROTO_AT: + l->linktype = CFCTRL_SRV_VEI; + l->endpoint = (s->sockaddr.u.at.type >> 2) & 0x3; + l->chtype = s->sockaddr.u.at.type & 0x3; + break; + case CAIFPROTO_DATAGRAM: + l->linktype = CFCTRL_SRV_DATAGRAM; + l->chtype = 0x00; + l->u.datagram.connid = s->sockaddr.u.dgm.connection_id; + break; + case CAIFPROTO_DATAGRAM_LOOP: + l->linktype = CFCTRL_SRV_DATAGRAM; + l->chtype = 0x03; + l->endpoint = 0x00; + l->u.datagram.connid = s->sockaddr.u.dgm.connection_id; + break; + case CAIFPROTO_RFM: + l->linktype = CFCTRL_SRV_RFM; + l->u.datagram.connid = s->sockaddr.u.rfm.connection_id; + strncpy(l->u.rfm.volume, s->sockaddr.u.rfm.volume, + sizeof(l->u.rfm.volume)-1); + l->u.rfm.volume[sizeof(l->u.rfm.volume)-1] = 0; + break; + case CAIFPROTO_UTIL: + l->linktype = CFCTRL_SRV_UTIL; + l->endpoint = 0x00; + l->chtype = 0x00; + strncpy(l->u.utility.name, s->sockaddr.u.util.service, + sizeof(l->u.utility.name)-1); + l->u.utility.name[sizeof(l->u.utility.name)-1] = 0; + caif_assert(sizeof(l->u.utility.name) > 10); + l->u.utility.paramlen = s->param.size; + if (l->u.utility.paramlen > sizeof(l->u.utility.params)) + l->u.utility.paramlen = sizeof(l->u.utility.params); + + memcpy(l->u.utility.params, s->param.data, + l->u.utility.paramlen); + + break; + case CAIFPROTO_DEBUG: + l->linktype = CFCTRL_SRV_DBG; + l->endpoint = s->sockaddr.u.dbg.service; + l->chtype = s->sockaddr.u.dbg.type; + break; + default: + return -EINVAL; + } + return 0; +} + +int caif_connect_client(struct net *net, struct caif_connect_request *conn_req, + struct cflayer *adap_layer, int *ifindex, int *proto_head, int *proto_tail) { struct cflayer *frml; + struct cfcnfg_phyinfo *phy; + int err; + struct cfctrl_link_param param; + struct cfcnfg *cfg = get_cfcnfg(net); + caif_assert(cfg != NULL); + + rcu_read_lock(); + err = caif_connect_req_to_link_param(cfg, conn_req, ¶m); + if (err) + goto unlock; + + phy = cfcnfg_get_phyinfo_rcu(cfg, param.phyid); + if (!phy) { + err = -ENODEV; + goto unlock; + } + err = -EINVAL; + if (adap_layer == NULL) { pr_err("adap_layer is zero\n"); - return -EINVAL; + goto unlock; } if (adap_layer->receive == NULL) { pr_err("adap_layer->receive is NULL\n"); - return -EINVAL; + goto unlock; } if (adap_layer->ctrlcmd == NULL) { pr_err("adap_layer->ctrlcmd == NULL\n"); - return -EINVAL; + goto unlock; } - frml = cnfg->phy_layers[param->phyid].frm_layer; + + err = -ENODEV; + frml = phy->frm_layer; if (frml == NULL) { pr_err("Specified PHY type does not exist!\n"); - return -ENODEV; + goto unlock; } - caif_assert(param->phyid == cnfg->phy_layers[param->phyid].id); - caif_assert(cnfg->phy_layers[param->phyid].frm_layer->id == - param->phyid); - caif_assert(cnfg->phy_layers[param->phyid].phy_layer->id == - param->phyid); + caif_assert(param.phyid == phy->id); + caif_assert(phy->frm_layer->id == + param.phyid); + caif_assert(phy->phy_layer->id == + param.phyid); - *ifindex = cnfg->phy_layers[param->phyid].ifindex; + *ifindex = phy->ifindex; + *proto_tail = 2; *proto_head = - protohead[param->linktype]+ - (cnfg->phy_layers[param->phyid].use_stx ? 1 : 0); - *proto_tail = 2; + protohead[param.linktype] + (phy->use_stx ? 1 : 0); + + rcu_read_unlock(); /* FIXME: ENUMERATE INITIALLY WHEN ACTIVATING PHYSICAL INTERFACE */ - cfctrl_enum_req(cnfg->ctrl, param->phyid); - return cfctrl_linkup_request(cnfg->ctrl, param, adap_layer); + cfctrl_enum_req(cfg->ctrl, param.phyid); + return cfctrl_linkup_request(cfg->ctrl, ¶m, adap_layer); + +unlock: + rcu_read_unlock(); + return err; } -EXPORT_SYMBOL(cfcnfg_add_adaptation_layer); +EXPORT_SYMBOL(caif_connect_client); static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id, struct cflayer *adapt_layer) @@ -315,32 +380,45 @@ static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id, static void cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv, - u8 phyid, struct cflayer *adapt_layer) + u8 phyid, struct cflayer *adapt_layer) { struct cfcnfg *cnfg = container_obj(layer); struct cflayer *servicel = NULL; struct cfcnfg_phyinfo *phyinfo; struct net_device *netdev; + if (channel_id == 0) { + pr_warn("received channel_id zero\n"); + if (adapt_layer != NULL && adapt_layer->ctrlcmd != NULL) + adapt_layer->ctrlcmd(adapt_layer, + CAIF_CTRLCMD_INIT_FAIL_RSP, 0); + return; + } + + rcu_read_lock(); + if (adapt_layer == NULL) { - pr_debug("link setup response but no client exist, send linkdown back\n"); + pr_debug("link setup response but no client exist," + "send linkdown back\n"); cfctrl_linkdown_req(cnfg->ctrl, channel_id, NULL); - return; + goto unlock; } caif_assert(cnfg != NULL); caif_assert(phyid != 0); - phyinfo = &cnfg->phy_layers[phyid]; + + phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phyid); + if (phyinfo == NULL) { + pr_err("ERROR: Link Layer Device dissapeared" + "while connecting\n"); + goto unlock; + } + + caif_assert(phyinfo != NULL); caif_assert(phyinfo->id == phyid); caif_assert(phyinfo->phy_layer != NULL); caif_assert(phyinfo->phy_layer->id == phyid); - phyinfo->phy_ref_count++; - if (phyinfo->phy_ref_count == 1 && - phyinfo->phy_layer->modemcmd != NULL) { - phyinfo->phy_layer->modemcmd(phyinfo->phy_layer, - _CAIF_MODEMCMD_PHYIF_USEFULL); - } adapt_layer->id = channel_id; switch (serv) { @@ -348,7 +426,8 @@ cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv, servicel = cfvei_create(channel_id, &phyinfo->dev_info); break; case CFCTRL_SRV_DATAGRAM: - servicel = cfdgml_create(channel_id, &phyinfo->dev_info); + servicel = cfdgml_create(channel_id, + &phyinfo->dev_info); break; case CFCTRL_SRV_RFM: netdev = phyinfo->dev_info.dev; @@ -365,94 +444,92 @@ cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv, servicel = cfdbgl_create(channel_id, &phyinfo->dev_info); break; default: - pr_err("Protocol error. Link setup response - unknown channel type\n"); - return; + pr_err("Protocol error. Link setup response " + "- unknown channel type\n"); + goto unlock; } if (!servicel) { pr_warn("Out of memory\n"); - return; + goto unlock; } layer_set_dn(servicel, cnfg->mux); cfmuxl_set_uplayer(cnfg->mux, servicel, channel_id); layer_set_up(servicel, adapt_layer); layer_set_dn(adapt_layer, servicel); - cfsrvl_get(servicel); + + rcu_read_unlock(); + servicel->ctrlcmd(servicel, CAIF_CTRLCMD_INIT_RSP, 0); + return; +unlock: + rcu_read_unlock(); } void cfcnfg_add_phy_layer(struct cfcnfg *cnfg, enum cfcnfg_phy_type phy_type, struct net_device *dev, struct cflayer *phy_layer, - u16 *phyid, enum cfcnfg_phy_preference pref, + enum cfcnfg_phy_preference pref, bool fcs, bool stx) { struct cflayer *frml; struct cflayer *phy_driver = NULL; + struct cfcnfg_phyinfo *phyinfo; int i; + u8 phyid; + mutex_lock(&cnfg->lock); - if (cnfg->phy_layers[cnfg->last_phyid].frm_layer == NULL) { - *phyid = cnfg->last_phyid; - - /* range: * 1..(MAX_PHY_LAYERS-1) */ - cnfg->last_phyid = - (cnfg->last_phyid % (MAX_PHY_LAYERS - 1)) + 1; - } else { - *phyid = 0; - for (i = 1; i < MAX_PHY_LAYERS; i++) { - if (cnfg->phy_layers[i].frm_layer == NULL) { - *phyid = i; - break; - } - } - } - if (*phyid == 0) { - pr_err("No Available PHY ID\n"); - return; + /* CAIF protocol allow maximum 6 link-layers */ + for (i = 0; i < 7; i++) { + phyid = (dev->ifindex + i) & 0x7; + if (phyid == 0) + continue; + if (cfcnfg_get_phyinfo_rcu(cnfg, phyid) == NULL) + goto got_phyid; } + pr_warn("Too many CAIF Link Layers (max 6)\n"); + goto out; + +got_phyid: + phyinfo = kzalloc(sizeof(struct cfcnfg_phyinfo), GFP_ATOMIC); switch (phy_type) { case CFPHYTYPE_FRAG: phy_driver = - cfserl_create(CFPHYTYPE_FRAG, *phyid, stx); + cfserl_create(CFPHYTYPE_FRAG, phyid, stx); if (!phy_driver) { pr_warn("Out of memory\n"); - return; + goto out; } - break; case CFPHYTYPE_CAIF: phy_driver = NULL; break; default: - pr_err("%d\n", phy_type); - return; - break; + goto out; } + phy_layer->id = phyid; + phyinfo->pref = pref; + phyinfo->id = phyid; + phyinfo->dev_info.id = phyid; + phyinfo->dev_info.dev = dev; + phyinfo->phy_layer = phy_layer; + phyinfo->ifindex = dev->ifindex; + phyinfo->use_stx = stx; + phyinfo->use_fcs = fcs; + + frml = cffrml_create(phyid, fcs); - phy_layer->id = *phyid; - cnfg->phy_layers[*phyid].pref = pref; - cnfg->phy_layers[*phyid].id = *phyid; - cnfg->phy_layers[*phyid].dev_info.id = *phyid; - cnfg->phy_layers[*phyid].dev_info.dev = dev; - cnfg->phy_layers[*phyid].phy_layer = phy_layer; - cnfg->phy_layers[*phyid].phy_ref_count = 0; - cnfg->phy_layers[*phyid].ifindex = dev->ifindex; - cnfg->phy_layers[*phyid].use_stx = stx; - cnfg->phy_layers[*phyid].use_fcs = fcs; - - phy_layer->type = phy_type; - frml = cffrml_create(*phyid, fcs); if (!frml) { pr_warn("Out of memory\n"); - return; + kfree(phyinfo); + goto out; } - cnfg->phy_layers[*phyid].frm_layer = frml; - cfmuxl_set_dnlayer(cnfg->mux, frml, *phyid); + phyinfo->frm_layer = frml; layer_set_up(frml, cnfg->mux); if (phy_driver != NULL) { - phy_driver->id = *phyid; + phy_driver->id = phyid; layer_set_dn(frml, phy_driver); layer_set_up(phy_driver, frml); layer_set_dn(phy_driver, phy_layer); @@ -461,33 +538,95 @@ cfcnfg_add_phy_layer(struct cfcnfg *cnfg, enum cfcnfg_phy_type phy_type, layer_set_dn(frml, phy_layer); layer_set_up(phy_layer, frml); } + + list_add_rcu(&phyinfo->node, &cnfg->phys); +out: + mutex_unlock(&cnfg->lock); } EXPORT_SYMBOL(cfcnfg_add_phy_layer); +int cfcnfg_set_phy_state(struct cfcnfg *cnfg, struct cflayer *phy_layer, + bool up) +{ + struct cfcnfg_phyinfo *phyinfo; + + rcu_read_lock(); + phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phy_layer->id); + if (phyinfo == NULL) { + rcu_read_unlock(); + return -ENODEV; + } + + if (phyinfo->up == up) { + rcu_read_unlock(); + return 0; + } + phyinfo->up = up; + + if (up) { + cffrml_hold(phyinfo->frm_layer); + cfmuxl_set_dnlayer(cnfg->mux, phyinfo->frm_layer, + phy_layer->id); + } else { + cfmuxl_remove_dnlayer(cnfg->mux, phy_layer->id); + cffrml_put(phyinfo->frm_layer); + } + + rcu_read_unlock(); + return 0; +} +EXPORT_SYMBOL(cfcnfg_set_phy_state); + int cfcnfg_del_phy_layer(struct cfcnfg *cnfg, struct cflayer *phy_layer) { struct cflayer *frml, *frml_dn; u16 phyid; + struct cfcnfg_phyinfo *phyinfo; + + might_sleep(); + + mutex_lock(&cnfg->lock); + phyid = phy_layer->id; - caif_assert(phyid == cnfg->phy_layers[phyid].id); - caif_assert(phy_layer == cnfg->phy_layers[phyid].phy_layer); + phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phyid); + + if (phyinfo == NULL) { + mutex_unlock(&cnfg->lock); + return 0; + } + caif_assert(phyid == phyinfo->id); + caif_assert(phy_layer == phyinfo->phy_layer); caif_assert(phy_layer->id == phyid); - caif_assert(cnfg->phy_layers[phyid].frm_layer->id == phyid); + caif_assert(phyinfo->frm_layer->id == phyid); + + list_del_rcu(&phyinfo->node); + synchronize_rcu(); - memset(&cnfg->phy_layers[phy_layer->id], 0, - sizeof(struct cfcnfg_phyinfo)); - frml = cfmuxl_remove_dnlayer(cnfg->mux, phy_layer->id); + /* Fail if reference count is not zero */ + if (cffrml_refcnt_read(phyinfo->frm_layer) != 0) { + pr_info("Wait for device inuse\n"); + list_add_rcu(&phyinfo->node, &cnfg->phys); + mutex_unlock(&cnfg->lock); + return -EAGAIN; + } + + frml = phyinfo->frm_layer; frml_dn = frml->dn; cffrml_set_uplayer(frml, NULL); cffrml_set_dnlayer(frml, NULL); - kfree(frml); - if (phy_layer != frml_dn) { layer_set_up(frml_dn, NULL); layer_set_dn(frml_dn, NULL); - kfree(frml_dn); } layer_set_up(phy_layer, NULL); + + if (phyinfo->phy_layer != frml_dn) + kfree(frml_dn); + + cffrml_free(frml); + kfree(phyinfo); + mutex_unlock(&cnfg->lock); + return 0; } EXPORT_SYMBOL(cfcnfg_del_phy_layer); diff --git a/net/caif/cfctrl.c b/net/caif/cfctrl.c index 397a2c099e2..e22671bed66 100644 --- a/net/caif/cfctrl.c +++ b/net/caif/cfctrl.c @@ -17,7 +17,6 @@ #define UTILITY_NAME_LENGTH 16 #define CFPKT_CTRL_PKT_LEN 20 - #ifdef CAIF_NO_LOOP static int handle_loop(struct cfctrl *ctrl, int cmd, struct cfpkt *pkt){ @@ -51,13 +50,29 @@ struct cflayer *cfctrl_create(void) this->serv.layer.receive = cfctrl_recv; sprintf(this->serv.layer.name, "ctrl"); this->serv.layer.ctrlcmd = cfctrl_ctrlcmd; +#ifndef CAIF_NO_LOOP spin_lock_init(&this->loop_linkid_lock); + this->loop_linkid = 1; +#endif spin_lock_init(&this->info_list_lock); INIT_LIST_HEAD(&this->list); - this->loop_linkid = 1; return &this->serv.layer; } +void cfctrl_remove(struct cflayer *layer) +{ + struct cfctrl_request_info *p, *tmp; + struct cfctrl *ctrl = container_obj(layer); + + spin_lock_bh(&ctrl->info_list_lock); + list_for_each_entry_safe(p, tmp, &ctrl->list, list) { + list_del(&p->list); + kfree(p); + } + spin_unlock_bh(&ctrl->info_list_lock); + kfree(layer); +} + static bool param_eq(const struct cfctrl_link_param *p1, const struct cfctrl_link_param *p2) { @@ -116,11 +131,11 @@ static bool cfctrl_req_eq(const struct cfctrl_request_info *r1, static void cfctrl_insert_req(struct cfctrl *ctrl, struct cfctrl_request_info *req) { - spin_lock(&ctrl->info_list_lock); + spin_lock_bh(&ctrl->info_list_lock); atomic_inc(&ctrl->req_seq_no); req->sequence_no = atomic_read(&ctrl->req_seq_no); list_add_tail(&req->list, &ctrl->list); - spin_unlock(&ctrl->info_list_lock); + spin_unlock_bh(&ctrl->info_list_lock); } /* Compare and remove request */ @@ -129,7 +144,6 @@ static struct cfctrl_request_info *cfctrl_remove_req(struct cfctrl *ctrl, { struct cfctrl_request_info *p, *tmp, *first; - spin_lock(&ctrl->info_list_lock); first = list_first_entry(&ctrl->list, struct cfctrl_request_info, list); list_for_each_entry_safe(p, tmp, &ctrl->list, list) { @@ -145,7 +159,6 @@ static struct cfctrl_request_info *cfctrl_remove_req(struct cfctrl *ctrl, } p = NULL; out: - spin_unlock(&ctrl->info_list_lock); return p; } @@ -165,24 +178,23 @@ static void init_info(struct caif_payload_info *info, struct cfctrl *cfctrl) void cfctrl_enum_req(struct cflayer *layer, u8 physlinkid) { struct cfctrl *cfctrl = container_obj(layer); - int ret; struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN); + struct cflayer *dn = cfctrl->serv.layer.dn; if (!pkt) { pr_warn("Out of memory\n"); return; } + if (!dn) { + pr_debug("not able to send enum request\n"); + return; + } caif_assert(offsetof(struct cfctrl, serv.layer) == 0); init_info(cfpkt_info(pkt), cfctrl); cfpkt_info(pkt)->dev_info->id = physlinkid; cfctrl->serv.dev_info.id = physlinkid; cfpkt_addbdy(pkt, CFCTRL_CMD_ENUM); cfpkt_addbdy(pkt, physlinkid); - ret = - cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt); - if (ret < 0) { - pr_err("Could not transmit enum message\n"); - cfpkt_destroy(pkt); - } + dn->transmit(dn, pkt); } int cfctrl_linkup_request(struct cflayer *layer, @@ -196,14 +208,29 @@ int cfctrl_linkup_request(struct cflayer *layer, struct cfctrl_request_info *req; int ret; char utility_name[16]; - struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN); + struct cfpkt *pkt; + struct cflayer *dn = cfctrl->serv.layer.dn; + + if (!dn) { + pr_debug("not able to send linkup request\n"); + return -ENODEV; + } + + if (cfctrl_cancel_req(layer, user_layer) > 0) { + /* Slight Paranoia, check if already connecting */ + pr_err("Duplicate connect request for same client\n"); + WARN_ON(1); + return -EALREADY; + } + + pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN); if (!pkt) { pr_warn("Out of memory\n"); return -ENOMEM; } cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_SETUP); - cfpkt_addbdy(pkt, (param->chtype << 4) + param->linktype); - cfpkt_addbdy(pkt, (param->priority << 3) + param->phyid); + cfpkt_addbdy(pkt, (param->chtype << 4) | param->linktype); + cfpkt_addbdy(pkt, (param->priority << 3) | param->phyid); cfpkt_addbdy(pkt, param->endpoint & 0x03); switch (param->linktype) { @@ -264,11 +291,15 @@ int cfctrl_linkup_request(struct cflayer *layer, */ cfpkt_info(pkt)->dev_info->id = param->phyid; ret = - cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt); + dn->transmit(dn, pkt); if (ret < 0) { - pr_err("Could not transmit linksetup request\n"); - cfpkt_destroy(pkt); - return -ENODEV; + int count; + + count = cfctrl_cancel_req(&cfctrl->serv.layer, + user_layer); + if (count != 1) + pr_err("Could not remove request (%d)", count); + return -ENODEV; } return 0; } @@ -279,37 +310,46 @@ int cfctrl_linkdown_req(struct cflayer *layer, u8 channelid, int ret; struct cfctrl *cfctrl = container_obj(layer); struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN); + struct cflayer *dn = cfctrl->serv.layer.dn; + if (!pkt) { pr_warn("Out of memory\n"); return -ENOMEM; } + + if (!dn) { + pr_debug("not able to send link-down request\n"); + return -ENODEV; + } + cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_DESTROY); cfpkt_addbdy(pkt, channelid); init_info(cfpkt_info(pkt), cfctrl); ret = - cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt); - if (ret < 0) { - pr_err("Could not transmit link-down request\n"); - cfpkt_destroy(pkt); - } + dn->transmit(dn, pkt); +#ifndef CAIF_NO_LOOP + cfctrl->loop_linkused[channelid] = 0; +#endif return ret; } -void cfctrl_cancel_req(struct cflayer *layr, struct cflayer *adap_layer) +int cfctrl_cancel_req(struct cflayer *layr, struct cflayer *adap_layer) { struct cfctrl_request_info *p, *tmp; struct cfctrl *ctrl = container_obj(layr); - spin_lock(&ctrl->info_list_lock); + int found = 0; + spin_lock_bh(&ctrl->info_list_lock); list_for_each_entry_safe(p, tmp, &ctrl->list, list) { if (p->client_layer == adap_layer) { - pr_debug("cancel req :%d\n", p->sequence_no); list_del(&p->list); kfree(p); + found++; } } - spin_unlock(&ctrl->info_list_lock); + spin_unlock_bh(&ctrl->info_list_lock); + return found; } static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt) @@ -328,7 +368,8 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt) cfpkt_extr_head(pkt, &cmdrsp, 1); cmd = cmdrsp & CFCTRL_CMD_MASK; if (cmd != CFCTRL_CMD_LINK_ERR - && CFCTRL_RSP_BIT != (CFCTRL_RSP_BIT & cmdrsp)) { + && CFCTRL_RSP_BIT != (CFCTRL_RSP_BIT & cmdrsp) + && CFCTRL_ERR_BIT != (CFCTRL_ERR_BIT & cmdrsp)) { if (handle_loop(cfctrl, cmd, pkt) != 0) cmdrsp |= CFCTRL_ERR_BIT; } @@ -454,18 +495,20 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt) cfpkt_extr_head(pkt, ¶m, len); break; default: - pr_warn("Request setup - invalid link type (%d)\n", + pr_warn("Request setup, invalid type (%d)\n", serv); goto error; } rsp.cmd = cmd; rsp.param = linkparam; + spin_lock_bh(&cfctrl->info_list_lock); req = cfctrl_remove_req(cfctrl, &rsp); if (CFCTRL_ERR_BIT == (CFCTRL_ERR_BIT & cmdrsp) || cfpkt_erroneous(pkt)) { - pr_err("Invalid O/E bit or parse error on CAIF control channel\n"); + pr_err("Invalid O/E bit or parse error " + "on CAIF control channel\n"); cfctrl->res.reject_rsp(cfctrl->serv.layer.up, 0, req ? req->client_layer @@ -480,6 +523,8 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt) if (req != NULL) kfree(req); + + spin_unlock_bh(&cfctrl->info_list_lock); } break; case CFCTRL_CMD_LINK_DESTROY: @@ -523,12 +568,28 @@ static void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, switch (ctrl) { case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND: case CAIF_CTRLCMD_FLOW_OFF_IND: - spin_lock(&this->info_list_lock); - if (!list_empty(&this->list)) { + spin_lock_bh(&this->info_list_lock); + if (!list_empty(&this->list)) pr_debug("Received flow off in control layer\n"); + spin_unlock_bh(&this->info_list_lock); + break; + case _CAIF_CTRLCMD_PHYIF_DOWN_IND: { + struct cfctrl_request_info *p, *tmp; + + /* Find all connect request and report failure */ + spin_lock_bh(&this->info_list_lock); + list_for_each_entry_safe(p, tmp, &this->list, list) { + if (p->param.phyid == phyid) { + list_del(&p->list); + p->client_layer->ctrlcmd(p->client_layer, + CAIF_CTRLCMD_INIT_FAIL_RSP, + phyid); + kfree(p); + } } - spin_unlock(&this->info_list_lock); + spin_unlock_bh(&this->info_list_lock); break; + } default: break; } @@ -538,27 +599,33 @@ static void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, static int handle_loop(struct cfctrl *ctrl, int cmd, struct cfpkt *pkt) { static int last_linkid; + static int dec; u8 linkid, linktype, tmp; switch (cmd) { case CFCTRL_CMD_LINK_SETUP: - spin_lock(&ctrl->loop_linkid_lock); - for (linkid = last_linkid + 1; linkid < 255; linkid++) - if (!ctrl->loop_linkused[linkid]) - goto found; - for (linkid = last_linkid - 1; linkid > 0; linkid--) + spin_lock_bh(&ctrl->loop_linkid_lock); + if (!dec) { + for (linkid = last_linkid + 1; linkid < 254; linkid++) + if (!ctrl->loop_linkused[linkid]) + goto found; + } + dec = 1; + for (linkid = last_linkid - 1; linkid > 1; linkid--) if (!ctrl->loop_linkused[linkid]) goto found; - spin_unlock(&ctrl->loop_linkid_lock); - pr_err("Out of link-ids\n"); - return -EINVAL; + spin_unlock_bh(&ctrl->loop_linkid_lock); + return -1; found: + if (linkid < 10) + dec = 0; + if (!ctrl->loop_linkused[linkid]) ctrl->loop_linkused[linkid] = 1; last_linkid = linkid; cfpkt_add_trail(pkt, &linkid, 1); - spin_unlock(&ctrl->loop_linkid_lock); + spin_unlock_bh(&ctrl->loop_linkid_lock); cfpkt_peek_head(pkt, &linktype, 1); if (linktype == CFCTRL_SRV_UTIL) { tmp = 0x01; @@ -568,10 +635,10 @@ found: break; case CFCTRL_CMD_LINK_DESTROY: - spin_lock(&ctrl->loop_linkid_lock); + spin_lock_bh(&ctrl->loop_linkid_lock); cfpkt_peek_head(pkt, &linkid, 1); ctrl->loop_linkused[linkid] = 0; - spin_unlock(&ctrl->loop_linkid_lock); + spin_unlock_bh(&ctrl->loop_linkid_lock); break; default: break; diff --git a/net/caif/cffrml.c b/net/caif/cffrml.c index 2423fed8e26..04204b20271 100644 --- a/net/caif/cffrml.c +++ b/net/caif/cffrml.c @@ -12,6 +12,7 @@ #include <linux/spinlock.h> #include <linux/slab.h> #include <linux/crc-ccitt.h> +#include <linux/netdevice.h> #include <net/caif/caif_layer.h> #include <net/caif/cfpkt.h> #include <net/caif/cffrml.h> @@ -21,6 +22,7 @@ struct cffrml { struct cflayer layer; bool dofcs; /* !< FCS active */ + int __percpu *pcpu_refcnt; }; static int cffrml_receive(struct cflayer *layr, struct cfpkt *pkt); @@ -37,6 +39,12 @@ struct cflayer *cffrml_create(u16 phyid, bool use_fcs) pr_warn("Out of memory\n"); return NULL; } + this->pcpu_refcnt = alloc_percpu(int); + if (this->pcpu_refcnt == NULL) { + kfree(this); + return NULL; + } + caif_assert(offsetof(struct cffrml, layer) == 0); memset(this, 0, sizeof(struct cflayer)); @@ -49,6 +57,13 @@ struct cflayer *cffrml_create(u16 phyid, bool use_fcs) return (struct cflayer *) this; } +void cffrml_free(struct cflayer *layer) +{ + struct cffrml *this = container_obj(layer); + free_percpu(this->pcpu_refcnt); + kfree(layer); +} + void cffrml_set_uplayer(struct cflayer *this, struct cflayer *up) { this->up = up; @@ -112,6 +127,13 @@ static int cffrml_receive(struct cflayer *layr, struct cfpkt *pkt) cfpkt_destroy(pkt); return -EPROTO; } + + if (layr->up == NULL) { + pr_err("Layr up is missing!\n"); + cfpkt_destroy(pkt); + return -EINVAL; + } + return layr->up->receive(layr->up, pkt); } @@ -134,14 +156,44 @@ static int cffrml_transmit(struct cflayer *layr, struct cfpkt *pkt) cfpkt_info(pkt)->hdr_len += 2; if (cfpkt_erroneous(pkt)) { pr_err("Packet is erroneous!\n"); + cfpkt_destroy(pkt); return -EPROTO; } + + if (layr->dn == NULL) { + cfpkt_destroy(pkt); + return -ENODEV; + + } return layr->dn->transmit(layr->dn, pkt); } static void cffrml_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, int phyid) { - if (layr->up->ctrlcmd) + if (layr->up && layr->up->ctrlcmd) layr->up->ctrlcmd(layr->up, ctrl, layr->id); } + +void cffrml_put(struct cflayer *layr) +{ + struct cffrml *this = container_obj(layr); + if (layr != NULL && this->pcpu_refcnt != NULL) + irqsafe_cpu_dec(*this->pcpu_refcnt); +} + +void cffrml_hold(struct cflayer *layr) +{ + struct cffrml *this = container_obj(layr); + if (layr != NULL && this->pcpu_refcnt != NULL) + irqsafe_cpu_inc(*this->pcpu_refcnt); +} + +int cffrml_refcnt_read(struct cflayer *layr) +{ + int i, refcnt = 0; + struct cffrml *this = container_obj(layr); + for_each_possible_cpu(i) + refcnt += *per_cpu_ptr(this->pcpu_refcnt, i); + return refcnt; +} diff --git a/net/caif/cfmuxl.c b/net/caif/cfmuxl.c index fc249746857..3a66b8c10e0 100644 --- a/net/caif/cfmuxl.c +++ b/net/caif/cfmuxl.c @@ -9,6 +9,7 @@ #include <linux/stddef.h> #include <linux/spinlock.h> #include <linux/slab.h> +#include <linux/rculist.h> #include <net/caif/cfpkt.h> #include <net/caif/cfmuxl.h> #include <net/caif/cfsrvl.h> @@ -61,76 +62,88 @@ struct cflayer *cfmuxl_create(void) return &this->layer; } -int cfmuxl_set_uplayer(struct cflayer *layr, struct cflayer *up, u8 linkid) -{ - struct cfmuxl *muxl = container_obj(layr); - spin_lock(&muxl->receive_lock); - cfsrvl_get(up); - list_add(&up->node, &muxl->srvl_list); - spin_unlock(&muxl->receive_lock); - return 0; -} - int cfmuxl_set_dnlayer(struct cflayer *layr, struct cflayer *dn, u8 phyid) { struct cfmuxl *muxl = (struct cfmuxl *) layr; - spin_lock(&muxl->transmit_lock); - list_add(&dn->node, &muxl->frml_list); - spin_unlock(&muxl->transmit_lock); + + spin_lock_bh(&muxl->transmit_lock); + list_add_rcu(&dn->node, &muxl->frml_list); + spin_unlock_bh(&muxl->transmit_lock); return 0; } static struct cflayer *get_from_id(struct list_head *list, u16 id) { - struct list_head *node; - struct cflayer *layer; - list_for_each(node, list) { - layer = list_entry(node, struct cflayer, node); - if (layer->id == id) - return layer; + struct cflayer *lyr; + list_for_each_entry_rcu(lyr, list, node) { + if (lyr->id == id) + return lyr; } + return NULL; } +int cfmuxl_set_uplayer(struct cflayer *layr, struct cflayer *up, u8 linkid) +{ + struct cfmuxl *muxl = container_obj(layr); + struct cflayer *old; + + spin_lock_bh(&muxl->receive_lock); + + /* Two entries with same id is wrong, so remove old layer from mux */ + old = get_from_id(&muxl->srvl_list, linkid); + if (old != NULL) + list_del_rcu(&old->node); + + list_add_rcu(&up->node, &muxl->srvl_list); + spin_unlock_bh(&muxl->receive_lock); + + return 0; +} + struct cflayer *cfmuxl_remove_dnlayer(struct cflayer *layr, u8 phyid) { struct cfmuxl *muxl = container_obj(layr); struct cflayer *dn; - spin_lock(&muxl->transmit_lock); - memset(muxl->dn_cache, 0, sizeof(muxl->dn_cache)); + int idx = phyid % DN_CACHE_SIZE; + + spin_lock_bh(&muxl->transmit_lock); + rcu_assign_pointer(muxl->dn_cache[idx], NULL); dn = get_from_id(&muxl->frml_list, phyid); - if (dn == NULL) { - spin_unlock(&muxl->transmit_lock); - return NULL; - } - list_del(&dn->node); + if (dn == NULL) + goto out; + + list_del_rcu(&dn->node); caif_assert(dn != NULL); - spin_unlock(&muxl->transmit_lock); +out: + spin_unlock_bh(&muxl->transmit_lock); return dn; } -/* Invariant: lock is taken */ static struct cflayer *get_up(struct cfmuxl *muxl, u16 id) { struct cflayer *up; int idx = id % UP_CACHE_SIZE; - up = muxl->up_cache[idx]; + up = rcu_dereference(muxl->up_cache[idx]); if (up == NULL || up->id != id) { + spin_lock_bh(&muxl->receive_lock); up = get_from_id(&muxl->srvl_list, id); - muxl->up_cache[idx] = up; + rcu_assign_pointer(muxl->up_cache[idx], up); + spin_unlock_bh(&muxl->receive_lock); } return up; } -/* Invariant: lock is taken */ static struct cflayer *get_dn(struct cfmuxl *muxl, struct dev_info *dev_info) { struct cflayer *dn; int idx = dev_info->id % DN_CACHE_SIZE; - dn = muxl->dn_cache[idx]; + dn = rcu_dereference(muxl->dn_cache[idx]); if (dn == NULL || dn->id != dev_info->id) { + spin_lock_bh(&muxl->transmit_lock); dn = get_from_id(&muxl->frml_list, dev_info->id); - muxl->dn_cache[idx] = dn; + rcu_assign_pointer(muxl->dn_cache[idx], dn); + spin_unlock_bh(&muxl->transmit_lock); } return dn; } @@ -139,15 +152,22 @@ struct cflayer *cfmuxl_remove_uplayer(struct cflayer *layr, u8 id) { struct cflayer *up; struct cfmuxl *muxl = container_obj(layr); - spin_lock(&muxl->receive_lock); - up = get_up(muxl, id); + int idx = id % UP_CACHE_SIZE; + + if (id == 0) { + pr_warn("Trying to remove control layer\n"); + return NULL; + } + + spin_lock_bh(&muxl->receive_lock); + up = get_from_id(&muxl->srvl_list, id); if (up == NULL) goto out; - memset(muxl->up_cache, 0, sizeof(muxl->up_cache)); - list_del(&up->node); - cfsrvl_put(up); + + rcu_assign_pointer(muxl->up_cache[idx], NULL); + list_del_rcu(&up->node); out: - spin_unlock(&muxl->receive_lock); + spin_unlock_bh(&muxl->receive_lock); return up; } @@ -162,22 +182,28 @@ static int cfmuxl_receive(struct cflayer *layr, struct cfpkt *pkt) cfpkt_destroy(pkt); return -EPROTO; } - - spin_lock(&muxl->receive_lock); + rcu_read_lock(); up = get_up(muxl, id); - spin_unlock(&muxl->receive_lock); + if (up == NULL) { - pr_info("Received data on unknown link ID = %d (0x%x) up == NULL", - id, id); + pr_debug("Received data on unknown link ID = %d (0x%x)" + " up == NULL", id, id); cfpkt_destroy(pkt); /* * Don't return ERROR, since modem misbehaves and sends out * flow on before linksetup response. */ + + rcu_read_unlock(); return /* CFGLU_EPROT; */ 0; } + + /* We can't hold rcu_lock during receive, so take a ref count instead */ cfsrvl_get(up); + rcu_read_unlock(); + ret = up->receive(up, pkt); + cfsrvl_put(up); return ret; } @@ -185,31 +211,63 @@ static int cfmuxl_receive(struct cflayer *layr, struct cfpkt *pkt) static int cfmuxl_transmit(struct cflayer *layr, struct cfpkt *pkt) { struct cfmuxl *muxl = container_obj(layr); + int err; u8 linkid; struct cflayer *dn; struct caif_payload_info *info = cfpkt_info(pkt); BUG_ON(!info); + + rcu_read_lock(); + dn = get_dn(muxl, info->dev_info); if (dn == NULL) { - pr_warn("Send data on unknown phy ID = %d (0x%x)\n", + pr_debug("Send data on unknown phy ID = %d (0x%x)\n", info->dev_info->id, info->dev_info->id); + rcu_read_unlock(); + cfpkt_destroy(pkt); return -ENOTCONN; } + info->hdr_len += 1; linkid = info->channel_id; cfpkt_add_head(pkt, &linkid, 1); - return dn->transmit(dn, pkt); + + /* We can't hold rcu_lock during receive, so take a ref count instead */ + cffrml_hold(dn); + + rcu_read_unlock(); + + err = dn->transmit(dn, pkt); + + cffrml_put(dn); + return err; } static void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, int phyid) { struct cfmuxl *muxl = container_obj(layr); - struct list_head *node, *next; struct cflayer *layer; - list_for_each_safe(node, next, &muxl->srvl_list) { - layer = list_entry(node, struct cflayer, node); - if (cfsrvl_phyid_match(layer, phyid)) + int idx; + + rcu_read_lock(); + list_for_each_entry_rcu(layer, &muxl->srvl_list, node) { + + if (cfsrvl_phyid_match(layer, phyid) && layer->ctrlcmd) { + + if ((ctrl == _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND || + ctrl == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND) && + layer->id != 0) { + + idx = layer->id % UP_CACHE_SIZE; + spin_lock_bh(&muxl->receive_lock); + rcu_assign_pointer(muxl->up_cache[idx], NULL); + list_del_rcu(&layer->node); + spin_unlock_bh(&muxl->receive_lock); + } + /* NOTE: ctrlcmd is not allowed to block */ layer->ctrlcmd(layer, ctrl, phyid); + } } + rcu_read_unlock(); } diff --git a/net/caif/cfpkt_skbuff.c b/net/caif/cfpkt_skbuff.c index 20c6cb3522e..75d4bfae1a7 100644 --- a/net/caif/cfpkt_skbuff.c +++ b/net/caif/cfpkt_skbuff.c @@ -97,21 +97,20 @@ inline struct cfpkt *cfpkt_create(u16 len) { return cfpkt_create_pfx(len + PKT_POSTFIX, PKT_PREFIX); } -EXPORT_SYMBOL(cfpkt_create); void cfpkt_destroy(struct cfpkt *pkt) { struct sk_buff *skb = pkt_to_skb(pkt); kfree_skb(skb); } -EXPORT_SYMBOL(cfpkt_destroy); + inline bool cfpkt_more(struct cfpkt *pkt) { struct sk_buff *skb = pkt_to_skb(pkt); return skb->len > 0; } -EXPORT_SYMBOL(cfpkt_more); + int cfpkt_peek_head(struct cfpkt *pkt, void *data, u16 len) { @@ -123,7 +122,6 @@ int cfpkt_peek_head(struct cfpkt *pkt, void *data, u16 len) return !cfpkt_extr_head(pkt, data, len) && !cfpkt_add_head(pkt, data, len); } -EXPORT_SYMBOL(cfpkt_peek_head); int cfpkt_extr_head(struct cfpkt *pkt, void *data, u16 len) { @@ -148,7 +146,6 @@ int cfpkt_extr_head(struct cfpkt *pkt, void *data, u16 len) memcpy(data, from, len); return 0; } -EXPORT_SYMBOL(cfpkt_extr_head); int cfpkt_extr_trail(struct cfpkt *pkt, void *dta, u16 len) { @@ -171,13 +168,13 @@ int cfpkt_extr_trail(struct cfpkt *pkt, void *dta, u16 len) memcpy(data, from, len); return 0; } -EXPORT_SYMBOL(cfpkt_extr_trail); + int cfpkt_pad_trail(struct cfpkt *pkt, u16 len) { return cfpkt_add_body(pkt, NULL, len); } -EXPORT_SYMBOL(cfpkt_pad_trail); + int cfpkt_add_body(struct cfpkt *pkt, const void *data, u16 len) { @@ -226,13 +223,11 @@ int cfpkt_add_body(struct cfpkt *pkt, const void *data, u16 len) memcpy(to, data, len); return 0; } -EXPORT_SYMBOL(cfpkt_add_body); inline int cfpkt_addbdy(struct cfpkt *pkt, u8 data) { return cfpkt_add_body(pkt, &data, 1); } -EXPORT_SYMBOL(cfpkt_addbdy); int cfpkt_add_head(struct cfpkt *pkt, const void *data2, u16 len) { @@ -259,20 +254,20 @@ int cfpkt_add_head(struct cfpkt *pkt, const void *data2, u16 len) memcpy(to, data, len); return 0; } -EXPORT_SYMBOL(cfpkt_add_head); + inline int cfpkt_add_trail(struct cfpkt *pkt, const void *data, u16 len) { return cfpkt_add_body(pkt, data, len); } -EXPORT_SYMBOL(cfpkt_add_trail); + inline u16 cfpkt_getlen(struct cfpkt *pkt) { struct sk_buff *skb = pkt_to_skb(pkt); return skb->len; } -EXPORT_SYMBOL(cfpkt_getlen); + inline u16 cfpkt_iterate(struct cfpkt *pkt, u16 (*iter_func)(u16, void *, u16), @@ -290,7 +285,7 @@ inline u16 cfpkt_iterate(struct cfpkt *pkt, } return iter_func(data, pkt->skb.data, cfpkt_getlen(pkt)); } -EXPORT_SYMBOL(cfpkt_iterate); + int cfpkt_setlen(struct cfpkt *pkt, u16 len) { @@ -315,7 +310,6 @@ int cfpkt_setlen(struct cfpkt *pkt, u16 len) return cfpkt_getlen(pkt); } -EXPORT_SYMBOL(cfpkt_setlen); struct cfpkt *cfpkt_append(struct cfpkt *dstpkt, struct cfpkt *addpkt, @@ -357,7 +351,6 @@ struct cfpkt *cfpkt_append(struct cfpkt *dstpkt, dst->len += addlen; return skb_to_pkt(dst); } -EXPORT_SYMBOL(cfpkt_append); struct cfpkt *cfpkt_split(struct cfpkt *pkt, u16 pos) { @@ -395,17 +388,13 @@ struct cfpkt *cfpkt_split(struct cfpkt *pkt, u16 pos) skb2->len += len2nd; return skb_to_pkt(skb2); } -EXPORT_SYMBOL(cfpkt_split); bool cfpkt_erroneous(struct cfpkt *pkt) { return cfpkt_priv(pkt)->erronous; } -EXPORT_SYMBOL(cfpkt_erroneous); - struct caif_payload_info *cfpkt_info(struct cfpkt *pkt) { return (struct caif_payload_info *)&pkt_to_skb(pkt)->cb; } -EXPORT_SYMBOL(cfpkt_info); diff --git a/net/caif/cfrfml.c b/net/caif/cfrfml.c index e2fb5fa7579..0deabb44005 100644 --- a/net/caif/cfrfml.c +++ b/net/caif/cfrfml.c @@ -31,9 +31,9 @@ struct cfrfml { spinlock_t sync; }; -static void cfrfml_release(struct kref *kref) +static void cfrfml_release(struct cflayer *layer) { - struct cfsrvl *srvl = container_of(kref, struct cfsrvl, ref); + struct cfsrvl *srvl = container_of(layer, struct cfsrvl, layer); struct cfrfml *rfml = container_obj(&srvl->layer); if (rfml->incomplete_frm) diff --git a/net/caif/cfsrvl.c b/net/caif/cfsrvl.c index 24ba392f203..535a1e72b36 100644 --- a/net/caif/cfsrvl.c +++ b/net/caif/cfsrvl.c @@ -10,6 +10,7 @@ #include <linux/types.h> #include <linux/errno.h> #include <linux/slab.h> +#include <linux/module.h> #include <net/caif/caif_layer.h> #include <net/caif/cfsrvl.h> #include <net/caif/cfpkt.h> @@ -27,8 +28,8 @@ static void cfservl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, { struct cfsrvl *service = container_obj(layr); - caif_assert(layr->up != NULL); - caif_assert(layr->up->ctrlcmd != NULL); + if (layr->up == NULL || layr->up->ctrlcmd == NULL) + return; switch (ctrl) { case CAIF_CTRLCMD_INIT_RSP: @@ -151,9 +152,9 @@ static int cfservl_modemcmd(struct cflayer *layr, enum caif_modemcmd ctrl) return -EINVAL; } -static void cfsrvl_release(struct kref *kref) +static void cfsrvl_release(struct cflayer *layer) { - struct cfsrvl *service = container_of(kref, struct cfsrvl, ref); + struct cfsrvl *service = container_of(layer, struct cfsrvl, layer); kfree(service); } @@ -173,10 +174,8 @@ void cfsrvl_init(struct cfsrvl *service, service->dev_info = *dev_info; service->supports_flowctrl = supports_flowctrl; service->release = cfsrvl_release; - kref_init(&service->ref); } - bool cfsrvl_ready(struct cfsrvl *service, int *err) { if (service->open && service->modem_flow_on && service->phy_flow_on) @@ -189,6 +188,7 @@ bool cfsrvl_ready(struct cfsrvl *service, int *err) *err = -EAGAIN; return false; } + u8 cfsrvl_getphyid(struct cflayer *layer) { struct cfsrvl *servl = container_obj(layer); @@ -200,3 +200,26 @@ bool cfsrvl_phyid_match(struct cflayer *layer, int phyid) struct cfsrvl *servl = container_obj(layer); return servl->dev_info.id == phyid; } + +void caif_free_client(struct cflayer *adap_layer) +{ + struct cfsrvl *servl; + if (adap_layer == NULL || adap_layer->dn == NULL) + return; + servl = container_obj(adap_layer->dn); + servl->release(&servl->layer); +} +EXPORT_SYMBOL(caif_free_client); + +void caif_client_register_refcnt(struct cflayer *adapt_layer, + void (*hold)(struct cflayer *lyr), + void (*put)(struct cflayer *lyr)) +{ + struct cfsrvl *service; + service = container_of(adapt_layer->dn, struct cfsrvl, layer); + + WARN_ON(adapt_layer == NULL || adapt_layer->dn == NULL); + service->hold = hold; + service->put = put; +} +EXPORT_SYMBOL(caif_client_register_refcnt); diff --git a/net/caif/cfveil.c b/net/caif/cfveil.c index 1a588cd818e..3ec83fbc288 100644 --- a/net/caif/cfveil.c +++ b/net/caif/cfveil.c @@ -82,13 +82,14 @@ static int cfvei_transmit(struct cflayer *layr, struct cfpkt *pkt) int ret; struct cfsrvl *service = container_obj(layr); if (!cfsrvl_ready(service, &ret)) - return ret; + goto err; caif_assert(layr->dn != NULL); caif_assert(layr->dn->transmit != NULL); if (cfpkt_add_head(pkt, &tmp, 1) < 0) { pr_err("Packet is erroneous!\n"); - return -EPROTO; + ret = -EPROTO; + goto err; } /* Add info-> for MUX-layer to route the packet out. */ @@ -97,4 +98,7 @@ static int cfvei_transmit(struct cflayer *layr, struct cfpkt *pkt) info->hdr_len = 1; info->dev_info = &service->dev_info; return layr->dn->transmit(layr->dn, pkt); +err: + cfpkt_destroy(pkt); + return ret; } diff --git a/net/caif/chnl_net.c b/net/caif/chnl_net.c index 6008d6dc18a..c628a57953c 100644 --- a/net/caif/chnl_net.c +++ b/net/caif/chnl_net.c @@ -9,6 +9,7 @@ #include <linux/version.h> #include <linux/fs.h> +#include <linux/hardirq.h> #include <linux/init.h> #include <linux/module.h> #include <linux/netdevice.h> @@ -20,7 +21,6 @@ #include <linux/caif/if_caif.h> #include <net/rtnetlink.h> #include <net/caif/caif_layer.h> -#include <net/caif/cfcnfg.h> #include <net/caif/cfpkt.h> #include <net/caif/caif_dev.h> @@ -84,10 +84,11 @@ static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt) if (!priv) return -EINVAL; + skb = (struct sk_buff *) cfpkt_tonative(pkt); + /* Get length of CAIF packet. */ - pktlen = cfpkt_getlen(pkt); + pktlen = skb->len; - skb = (struct sk_buff *) cfpkt_tonative(pkt); /* Pass some minimum information and * send the packet to the net stack. */ @@ -139,20 +140,29 @@ static void close_work(struct work_struct *work) struct chnl_net *dev = NULL; struct list_head *list_node; struct list_head *_tmp; - /* May be called with or without RTNL lock held */ - int islocked = rtnl_is_locked(); - if (!islocked) - rtnl_lock(); + + rtnl_lock(); list_for_each_safe(list_node, _tmp, &chnl_net_list) { dev = list_entry(list_node, struct chnl_net, list_field); if (dev->state == CAIF_SHUTDOWN) dev_close(dev->netdev); } - if (!islocked) - rtnl_unlock(); + rtnl_unlock(); } static DECLARE_WORK(close_worker, close_work); +static void chnl_hold(struct cflayer *lyr) +{ + struct chnl_net *priv = container_of(lyr, struct chnl_net, chnl); + dev_hold(priv->netdev); +} + +static void chnl_put(struct cflayer *lyr) +{ + struct chnl_net *priv = container_of(lyr, struct chnl_net, chnl); + dev_put(priv->netdev); +} + static void chnl_flowctrl_cb(struct cflayer *layr, enum caif_ctrlcmd flow, int phyid) { @@ -190,6 +200,7 @@ static void chnl_flowctrl_cb(struct cflayer *layr, enum caif_ctrlcmd flow, netif_wake_queue(priv->netdev); break; case CAIF_CTRLCMD_INIT_RSP: + caif_client_register_refcnt(&priv->chnl, chnl_hold, chnl_put); priv->state = CAIF_CONNECTED; priv->flowenabled = true; netif_wake_queue(priv->netdev); @@ -257,8 +268,9 @@ static int chnl_net_open(struct net_device *dev) if (priv->state != CAIF_CONNECTING) { priv->state = CAIF_CONNECTING; - result = caif_connect_client(&priv->conn_req, &priv->chnl, - &llifindex, &headroom, &tailroom); + result = caif_connect_client(dev_net(dev), &priv->conn_req, + &priv->chnl, &llifindex, + &headroom, &tailroom); if (result != 0) { pr_debug("err: " "Unable to register and open device," @@ -314,7 +326,7 @@ static int chnl_net_open(struct net_device *dev) if (result == 0) { pr_debug("connect timeout\n"); - caif_disconnect_client(&priv->chnl); + caif_disconnect_client(dev_net(dev), &priv->chnl); priv->state = CAIF_DISCONNECTED; pr_debug("state disconnected\n"); result = -ETIMEDOUT; @@ -330,7 +342,7 @@ static int chnl_net_open(struct net_device *dev) return 0; error: - caif_disconnect_client(&priv->chnl); + caif_disconnect_client(dev_net(dev), &priv->chnl); priv->state = CAIF_DISCONNECTED; pr_debug("state disconnected\n"); return result; @@ -344,7 +356,7 @@ static int chnl_net_stop(struct net_device *dev) ASSERT_RTNL(); priv = netdev_priv(dev); priv->state = CAIF_DISCONNECTED; - caif_disconnect_client(&priv->chnl); + caif_disconnect_client(dev_net(dev), &priv->chnl); return 0; } @@ -373,11 +385,18 @@ static const struct net_device_ops netdev_ops = { .ndo_start_xmit = chnl_net_start_xmit, }; +static void chnl_net_destructor(struct net_device *dev) +{ + struct chnl_net *priv = netdev_priv(dev); + caif_free_client(&priv->chnl); + free_netdev(dev); +} + static void ipcaif_net_setup(struct net_device *dev) { struct chnl_net *priv; dev->netdev_ops = &netdev_ops; - dev->destructor = free_netdev; + dev->destructor = chnl_net_destructor; dev->flags |= IFF_NOARP; dev->flags |= IFF_POINTOPOINT; dev->mtu = GPRS_PDP_MTU; @@ -391,7 +410,7 @@ static void ipcaif_net_setup(struct net_device *dev) priv->conn_req.link_selector = CAIF_LINK_HIGH_BANDW; priv->conn_req.priority = CAIF_PRIO_LOW; /* Insert illegal value */ - priv->conn_req.sockaddr.u.dgm.connection_id = -1; + priv->conn_req.sockaddr.u.dgm.connection_id = 0; priv->flowenabled = false; init_waitqueue_head(&priv->netmgmt_wq); @@ -453,6 +472,10 @@ static int ipcaif_newlink(struct net *src_net, struct net_device *dev, pr_warn("device rtml registration failed\n"); else list_add(&caifdev->list_field, &chnl_net_list); + + /* Take ifindex as connection-id if null */ + if (caifdev->conn_req.sockaddr.u.dgm.connection_id == 0) + caifdev->conn_req.sockaddr.u.dgm.connection_id = dev->ifindex; return ret; } diff --git a/net/can/af_can.c b/net/can/af_can.c index a8dcaa49675..094fc5332d4 100644 --- a/net/can/af_can.c +++ b/net/can/af_can.c @@ -84,7 +84,7 @@ static DEFINE_SPINLOCK(can_rcvlists_lock); static struct kmem_cache *rcv_cache __read_mostly; /* table of registered CAN protocols */ -static struct can_proto *proto_tab[CAN_NPROTO] __read_mostly; +static const struct can_proto *proto_tab[CAN_NPROTO] __read_mostly; static DEFINE_MUTEX(proto_tab_lock); struct timer_list can_stattimer; /* timer for statistics update */ @@ -115,9 +115,9 @@ static void can_sock_destruct(struct sock *sk) skb_queue_purge(&sk->sk_receive_queue); } -static struct can_proto *can_try_module_get(int protocol) +static const struct can_proto *can_get_proto(int protocol) { - struct can_proto *cp; + const struct can_proto *cp; rcu_read_lock(); cp = rcu_dereference(proto_tab[protocol]); @@ -128,11 +128,16 @@ static struct can_proto *can_try_module_get(int protocol) return cp; } +static inline void can_put_proto(const struct can_proto *cp) +{ + module_put(cp->prot->owner); +} + static int can_create(struct net *net, struct socket *sock, int protocol, int kern) { struct sock *sk; - struct can_proto *cp; + const struct can_proto *cp; int err = 0; sock->state = SS_UNCONNECTED; @@ -143,7 +148,7 @@ static int can_create(struct net *net, struct socket *sock, int protocol, if (!net_eq(net, &init_net)) return -EAFNOSUPPORT; - cp = can_try_module_get(protocol); + cp = can_get_proto(protocol); #ifdef CONFIG_MODULES if (!cp) { @@ -160,7 +165,7 @@ static int can_create(struct net *net, struct socket *sock, int protocol, printk(KERN_ERR "can: request_module " "(can-proto-%d) failed.\n", protocol); - cp = can_try_module_get(protocol); + cp = can_get_proto(protocol); } #endif @@ -195,7 +200,7 @@ static int can_create(struct net *net, struct socket *sock, int protocol, } errout: - module_put(cp->prot->owner); + can_put_proto(cp); return err; } @@ -691,7 +696,7 @@ drop: * -EBUSY protocol already in use * -ENOBUF if proto_register() fails */ -int can_proto_register(struct can_proto *cp) +int can_proto_register(const struct can_proto *cp) { int proto = cp->protocol; int err = 0; @@ -728,7 +733,7 @@ EXPORT_SYMBOL(can_proto_register); * can_proto_unregister - unregister CAN transport protocol * @cp: pointer to CAN protocol structure */ -void can_proto_unregister(struct can_proto *cp) +void can_proto_unregister(const struct can_proto *cp) { int proto = cp->protocol; diff --git a/net/can/bcm.c b/net/can/bcm.c index 57b1aed7901..d6c8ae5b2e6 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -43,6 +43,7 @@ #include <linux/module.h> #include <linux/init.h> +#include <linux/interrupt.h> #include <linux/hrtimer.h> #include <linux/list.h> #include <linux/proc_fs.h> @@ -165,9 +166,9 @@ static int bcm_proc_show(struct seq_file *m, void *v) struct bcm_sock *bo = bcm_sk(sk); struct bcm_op *op; - seq_printf(m, ">>> socket %p", sk->sk_socket); - seq_printf(m, " / sk %p", sk); - seq_printf(m, " / bo %p", bo); + seq_printf(m, ">>> socket %pK", sk->sk_socket); + seq_printf(m, " / sk %pK", sk); + seq_printf(m, " / bo %pK", bo); seq_printf(m, " / dropped %lu", bo->dropped_usr_msgs); seq_printf(m, " / bound %s", bcm_proc_getifname(ifname, bo->ifindex)); seq_printf(m, " <<<\n"); @@ -1427,9 +1428,14 @@ static int bcm_init(struct sock *sk) static int bcm_release(struct socket *sock) { struct sock *sk = sock->sk; - struct bcm_sock *bo = bcm_sk(sk); + struct bcm_sock *bo; struct bcm_op *op, *next; + if (sk == NULL) + return 0; + + bo = bcm_sk(sk); + /* remove bcm_ops, timer, rx_unregister(), etc. */ unregister_netdevice_notifier(&bo->notifier); @@ -1596,7 +1602,7 @@ static struct proto bcm_proto __read_mostly = { .init = bcm_init, }; -static struct can_proto bcm_can_proto __read_mostly = { +static const struct can_proto bcm_can_proto = { .type = SOCK_DGRAM, .protocol = CAN_BCM, .ops = &bcm_ops, diff --git a/net/can/proc.c b/net/can/proc.c index f4265cc9c3f..0016f733969 100644 --- a/net/can/proc.c +++ b/net/can/proc.c @@ -204,12 +204,11 @@ static void can_print_rcvlist(struct seq_file *m, struct hlist_head *rx_list, hlist_for_each_entry_rcu(r, n, rx_list, list) { char *fmt = (r->can_id & CAN_EFF_FLAG)? - " %-5s %08X %08x %08x %08x %8ld %s\n" : - " %-5s %03X %08x %08lx %08lx %8ld %s\n"; + " %-5s %08x %08x %pK %pK %8ld %s\n" : + " %-5s %03x %08x %pK %pK %8ld %s\n"; seq_printf(m, fmt, DNAME(dev), r->can_id, r->mask, - (unsigned long)r->func, (unsigned long)r->data, - r->matches, r->ident); + r->func, r->data, r->matches, r->ident); } } diff --git a/net/can/raw.c b/net/can/raw.c index 649acfa7c70..dea99a6e596 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -305,7 +305,12 @@ static int raw_init(struct sock *sk) static int raw_release(struct socket *sock) { struct sock *sk = sock->sk; - struct raw_sock *ro = raw_sk(sk); + struct raw_sock *ro; + + if (!sk) + return 0; + + ro = raw_sk(sk); unregister_netdevice_notifier(&ro->notifier); @@ -769,7 +774,7 @@ static struct proto raw_proto __read_mostly = { .init = raw_init, }; -static struct can_proto raw_can_proto __read_mostly = { +static const struct can_proto raw_can_proto = { .type = SOCK_RAW, .protocol = CAN_RAW, .ops = &raw_ops, diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 05f357828a2..78b55f49de7 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -76,7 +76,8 @@ const char *ceph_pr_addr(const struct sockaddr_storage *ss) break; default: - sprintf(s, "(unknown sockaddr family %d)", (int)ss->ss_family); + snprintf(s, MAX_ADDR_STR_LEN, "(unknown sockaddr family %d)", + (int)ss->ss_family); } return s; @@ -598,7 +599,7 @@ static void prepare_write_keepalive(struct ceph_connection *con) * Connection negotiation. */ -static void prepare_connect_authorizer(struct ceph_connection *con) +static int prepare_connect_authorizer(struct ceph_connection *con) { void *auth_buf; int auth_len = 0; @@ -612,13 +613,20 @@ static void prepare_connect_authorizer(struct ceph_connection *con) con->auth_retry); mutex_lock(&con->mutex); + if (test_bit(CLOSED, &con->state) || + test_bit(OPENING, &con->state)) + return -EAGAIN; + con->out_connect.authorizer_protocol = cpu_to_le32(auth_protocol); con->out_connect.authorizer_len = cpu_to_le32(auth_len); - con->out_kvec[con->out_kvec_left].iov_base = auth_buf; - con->out_kvec[con->out_kvec_left].iov_len = auth_len; - con->out_kvec_left++; - con->out_kvec_bytes += auth_len; + if (auth_len) { + con->out_kvec[con->out_kvec_left].iov_base = auth_buf; + con->out_kvec[con->out_kvec_left].iov_len = auth_len; + con->out_kvec_left++; + con->out_kvec_bytes += auth_len; + } + return 0; } /* @@ -640,9 +648,9 @@ static void prepare_write_banner(struct ceph_messenger *msgr, set_bit(WRITE_PENDING, &con->state); } -static void prepare_write_connect(struct ceph_messenger *msgr, - struct ceph_connection *con, - int after_banner) +static int prepare_write_connect(struct ceph_messenger *msgr, + struct ceph_connection *con, + int after_banner) { unsigned global_seq = get_global_seq(con->msgr, 0); int proto; @@ -683,7 +691,7 @@ static void prepare_write_connect(struct ceph_messenger *msgr, con->out_more = 0; set_bit(WRITE_PENDING, &con->state); - prepare_connect_authorizer(con); + return prepare_connect_authorizer(con); } @@ -1065,8 +1073,10 @@ static void addr_set_port(struct sockaddr_storage *ss, int p) switch (ss->ss_family) { case AF_INET: ((struct sockaddr_in *)ss)->sin_port = htons(p); + break; case AF_INET6: ((struct sockaddr_in6 *)ss)->sin6_port = htons(p); + break; } } @@ -1216,6 +1226,7 @@ static int process_connect(struct ceph_connection *con) u64 sup_feat = con->msgr->supported_features; u64 req_feat = con->msgr->required_features; u64 server_feat = le64_to_cpu(con->in_reply.features); + int ret; dout("process_connect on %p tag %d\n", con, (int)con->in_tag); @@ -1250,7 +1261,9 @@ static int process_connect(struct ceph_connection *con) return -1; } con->auth_retry = 1; - prepare_write_connect(con->msgr, con, 0); + ret = prepare_write_connect(con->msgr, con, 0); + if (ret < 0) + return ret; prepare_read_connect(con); break; @@ -1277,6 +1290,9 @@ static int process_connect(struct ceph_connection *con) if (con->ops->peer_reset) con->ops->peer_reset(con); mutex_lock(&con->mutex); + if (test_bit(CLOSED, &con->state) || + test_bit(OPENING, &con->state)) + return -EAGAIN; break; case CEPH_MSGR_TAG_RETRY_SESSION: @@ -1341,7 +1357,9 @@ static int process_connect(struct ceph_connection *con) * to WAIT. This shouldn't happen if we are the * client. */ - pr_err("process_connect peer connecting WAIT\n"); + pr_err("process_connect got WAIT as client\n"); + con->error_msg = "protocol error, got WAIT as client"; + return -1; default: pr_err("connect protocol error, will retry\n"); @@ -1810,6 +1828,17 @@ static int try_read(struct ceph_connection *con) more: dout("try_read tag %d in_base_pos %d\n", (int)con->in_tag, con->in_base_pos); + + /* + * process_connect and process_message drop and re-take + * con->mutex. make sure we handle a racing close or reopen. + */ + if (test_bit(CLOSED, &con->state) || + test_bit(OPENING, &con->state)) { + ret = -EAGAIN; + goto out; + } + if (test_bit(CONNECTING, &con->state)) { if (!test_bit(NEGOTIATING, &con->state)) { dout("try_read connecting\n"); @@ -1938,8 +1967,10 @@ static void con_work(struct work_struct *work) { struct ceph_connection *con = container_of(work, struct ceph_connection, work.work); + int ret; mutex_lock(&con->mutex); +restart: if (test_and_clear_bit(BACKOFF, &con->state)) { dout("con_work %p backing off\n", con); if (queue_delayed_work(ceph_msgr_wq, &con->work, @@ -1969,18 +2000,31 @@ static void con_work(struct work_struct *work) con_close_socket(con); } - if (test_and_clear_bit(SOCK_CLOSED, &con->state) || - try_read(con) < 0 || - try_write(con) < 0) { - mutex_unlock(&con->mutex); - ceph_fault(con); /* error/fault path */ - goto done_unlocked; - } + if (test_and_clear_bit(SOCK_CLOSED, &con->state)) + goto fault; + + ret = try_read(con); + if (ret == -EAGAIN) + goto restart; + if (ret < 0) + goto fault; + + ret = try_write(con); + if (ret == -EAGAIN) + goto restart; + if (ret < 0) + goto fault; done: mutex_unlock(&con->mutex); done_unlocked: con->ops->put(con); + return; + +fault: + mutex_unlock(&con->mutex); + ceph_fault(con); /* error/fault path */ + goto done_unlocked; } @@ -2267,6 +2311,19 @@ struct ceph_msg *ceph_msg_new(int type, int front_len, gfp_t flags) m->more_to_follow = false; m->pool = NULL; + /* middle */ + m->middle = NULL; + + /* data */ + m->nr_pages = 0; + m->page_alignment = 0; + m->pages = NULL; + m->pagelist = NULL; + m->bio = NULL; + m->bio_iter = NULL; + m->bio_seg = 0; + m->trail = NULL; + /* front */ if (front_len) { if (front_len > PAGE_CACHE_SIZE) { @@ -2286,19 +2343,6 @@ struct ceph_msg *ceph_msg_new(int type, int front_len, gfp_t flags) } m->front.iov_len = front_len; - /* middle */ - m->middle = NULL; - - /* data */ - m->nr_pages = 0; - m->page_alignment = 0; - m->pages = NULL; - m->pagelist = NULL; - m->bio = NULL; - m->bio_iter = NULL; - m->bio_seg = 0; - m->trail = NULL; - dout("ceph_msg_new %p front %d\n", m, front_len); return m; diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 50af02737a3..6ea2b892f44 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -124,7 +124,7 @@ static void calc_layout(struct ceph_osd_client *osdc, ceph_calc_raw_layout(osdc, layout, vino.snap, off, plen, &bno, req, op); - sprintf(req->r_oid, "%llx.%08llx", vino.ino, bno); + snprintf(req->r_oid, sizeof(req->r_oid), "%llx.%08llx", vino.ino, bno); req->r_oid_len = strlen(req->r_oid); } @@ -470,8 +470,8 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, snapc, ops, use_mempool, GFP_NOFS, NULL, NULL); - if (IS_ERR(req)) - return req; + if (!req) + return NULL; /* calculate max write size */ calc_layout(osdc, vino, layout, off, plen, req, ops); @@ -579,9 +579,15 @@ static void __kick_osd_requests(struct ceph_osd_client *osdc, list_for_each_entry_safe(req, nreq, &osd->o_linger_requests, r_linger_osd) { - __unregister_linger_request(osdc, req); + /* + * reregister request prior to unregistering linger so + * that r_osd is preserved. + */ + BUG_ON(!list_empty(&req->r_req_lru_item)); __register_request(osdc, req); - list_move(&req->r_req_lru_item, &osdc->req_unsent); + list_add(&req->r_req_lru_item, &osdc->req_unsent); + list_add(&req->r_osd_item, &req->r_osd->o_requests); + __unregister_linger_request(osdc, req); dout("requeued lingering %p tid %llu osd%d\n", req, req->r_tid, osd->o_osd); } @@ -798,7 +804,7 @@ static void __register_request(struct ceph_osd_client *osdc, req->r_request->hdr.tid = cpu_to_le64(req->r_tid); INIT_LIST_HEAD(&req->r_req_lru_item); - dout("register_request %p tid %lld\n", req, req->r_tid); + dout("__register_request %p tid %lld\n", req, req->r_tid); __insert_request(osdc, req); ceph_osdc_get_request(req); osdc->num_requests++; @@ -1415,6 +1421,15 @@ void ceph_osdc_handle_map(struct ceph_osd_client *osdc, struct ceph_msg *msg) done: downgrade_write(&osdc->map_sem); ceph_monc_got_osdmap(&osdc->client->monc, osdc->osdmap->epoch); + + /* + * subscribe to subsequent osdmap updates if full to ensure + * we find out when we are no longer full and stop returning + * ENOSPC. + */ + if (ceph_osdmap_flag(osdc->osdmap, CEPH_OSDMAP_FULL)) + ceph_monc_request_next_osdmap(&osdc->client->monc); + send_queued(osdc); up_read(&osdc->map_sem); wake_up_all(&osdc->client->auth_wq); @@ -1671,8 +1686,14 @@ int ceph_osdc_start_request(struct ceph_osd_client *osdc, */ if (req->r_sent == 0) { rc = __map_request(osdc, req); - if (rc < 0) + if (rc < 0) { + if (nofail) { + dout("osdc_start_request failed map, " + " will retry %lld\n", req->r_tid); + rc = 0; + } goto out_unlock; + } if (req->r_osd == NULL) { dout("send_request %p no up osds in pg\n", req); ceph_monc_request_next_osdmap(&osdc->client->monc); diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c index 71603ac3dff..e97c3588c3e 100644 --- a/net/ceph/osdmap.c +++ b/net/ceph/osdmap.c @@ -765,7 +765,7 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, } map->epoch++; - map->modified = map->modified; + map->modified = modified; if (newcrush) { if (map->crush) crush_destroy(map->crush); @@ -830,15 +830,20 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, map->osd_addr[osd] = addr; } - /* new_down */ + /* new_state */ ceph_decode_32_safe(p, end, len, bad); while (len--) { u32 osd; + u8 xorstate; ceph_decode_32_safe(p, end, osd, bad); + xorstate = **(u8 **)p; (*p)++; /* clean flag */ - pr_info("osd%d down\n", osd); + if (xorstate == 0) + xorstate = CEPH_OSD_UP; + if (xorstate & CEPH_OSD_UP) + pr_info("osd%d down\n", osd); if (osd < map->max_osd) - map->osd_state[osd] &= ~CEPH_OSD_UP; + map->osd_state[osd] ^= xorstate; } /* new_weight */ diff --git a/net/compat.c b/net/compat.c index 3649d589536..c578d9382e1 100644 --- a/net/compat.c +++ b/net/compat.c @@ -722,11 +722,11 @@ EXPORT_SYMBOL(compat_mc_getsockopt); /* Argument list sizes for compat_sys_socketcall */ #define AL(x) ((x) * sizeof(u32)) -static unsigned char nas[20] = { +static unsigned char nas[21] = { AL(0), AL(3), AL(3), AL(3), AL(2), AL(3), AL(3), AL(3), AL(4), AL(4), AL(4), AL(6), AL(6), AL(2), AL(5), AL(5), AL(3), AL(3), - AL(4), AL(5) + AL(4), AL(5), AL(4) }; #undef AL @@ -735,6 +735,13 @@ asmlinkage long compat_sys_sendmsg(int fd, struct compat_msghdr __user *msg, uns return sys_sendmsg(fd, (struct msghdr __user *)msg, flags | MSG_CMSG_COMPAT); } +asmlinkage long compat_sys_sendmmsg(int fd, struct compat_mmsghdr __user *mmsg, + unsigned vlen, unsigned int flags) +{ + return __sys_sendmmsg(fd, (struct mmsghdr __user *)mmsg, vlen, + flags | MSG_CMSG_COMPAT); +} + asmlinkage long compat_sys_recvmsg(int fd, struct compat_msghdr __user *msg, unsigned int flags) { return sys_recvmsg(fd, (struct msghdr __user *)msg, flags | MSG_CMSG_COMPAT); @@ -780,7 +787,7 @@ asmlinkage long compat_sys_socketcall(int call, u32 __user *args) u32 a[6]; u32 a0, a1; - if (call < SYS_SOCKET || call > SYS_RECVMMSG) + if (call < SYS_SOCKET || call > SYS_SENDMMSG) return -EINVAL; if (copy_from_user(a, args, nas[call])) return -EFAULT; @@ -839,6 +846,9 @@ asmlinkage long compat_sys_socketcall(int call, u32 __user *args) case SYS_SENDMSG: ret = compat_sys_sendmsg(a0, compat_ptr(a1), a[2]); break; + case SYS_SENDMMSG: + ret = compat_sys_sendmmsg(a0, compat_ptr(a1), a[2], a[3]); + break; case SYS_RECVMSG: ret = compat_sys_recvmsg(a0, compat_ptr(a1), a[2]); break; diff --git a/net/core/dev.c b/net/core/dev.c index 3871bf69a38..b3f52d2f56d 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -948,7 +948,7 @@ int dev_alloc_name(struct net_device *dev, const char *name) } EXPORT_SYMBOL(dev_alloc_name); -static int dev_get_valid_name(struct net_device *dev, const char *name, bool fmt) +static int dev_get_valid_name(struct net_device *dev, const char *name) { struct net *net; @@ -958,7 +958,7 @@ static int dev_get_valid_name(struct net_device *dev, const char *name, bool fmt if (!dev_valid_name(name)) return -EINVAL; - if (fmt && strchr(name, '%')) + if (strchr(name, '%')) return dev_alloc_name(dev, name); else if (__dev_get_by_name(net, name)) return -EEXIST; @@ -995,7 +995,7 @@ int dev_change_name(struct net_device *dev, const char *newname) memcpy(oldname, dev->name, IFNAMSIZ); - err = dev_get_valid_name(dev, newname, 1); + err = dev_get_valid_name(dev, newname); if (err < 0) return err; @@ -1007,7 +1007,7 @@ rollback: } write_lock_bh(&dev_base_lock); - hlist_del(&dev->name_hlist); + hlist_del_rcu(&dev->name_hlist); write_unlock_bh(&dev_base_lock); synchronize_rcu(); @@ -1284,11 +1284,13 @@ static int dev_close_many(struct list_head *head) */ int dev_close(struct net_device *dev) { - LIST_HEAD(single); + if (dev->flags & IFF_UP) { + LIST_HEAD(single); - list_add(&dev->unreg_list, &single); - dev_close_many(&single); - list_del(&single); + list_add(&dev->unreg_list, &single); + dev_close_many(&single); + list_del(&single); + } return 0; } EXPORT_SYMBOL(dev_close); @@ -1306,6 +1308,13 @@ void dev_disable_lro(struct net_device *dev) { u32 flags; + /* + * If we're trying to disable lro on a vlan device + * use the underlying physical device instead + */ + if (is_vlan_dev(dev)) + dev = vlan_dev_real_dev(dev); + if (dev->ethtool_ops && dev->ethtool_ops->get_flags) flags = dev->ethtool_ops->get_flags(dev); else @@ -1315,7 +1324,8 @@ void dev_disable_lro(struct net_device *dev) return; __ethtool_set_flags(dev, flags & ~ETH_FLAG_LRO); - WARN_ON(dev->features & NETIF_F_LRO); + if (unlikely(dev->features & NETIF_F_LRO)) + netdev_WARN(dev, "failed to disable LRO!\n"); } EXPORT_SYMBOL(dev_disable_lro); @@ -2086,6 +2096,7 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, { const struct net_device_ops *ops = dev->netdev_ops; int rc = NETDEV_TX_OK; + unsigned int skb_len; if (likely(!skb->next)) { u32 features; @@ -2136,8 +2147,9 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, } } + skb_len = skb->len; rc = ops->ndo_start_xmit(skb, dev); - trace_net_dev_xmit(skb, rc); + trace_net_dev_xmit(skb, rc, dev, skb_len); if (rc == NETDEV_TX_OK) txq_trans_update(txq); return rc; @@ -2157,8 +2169,9 @@ gso: if (dev->priv_flags & IFF_XMIT_DST_RELEASE) skb_dst_drop(nskb); + skb_len = nskb->len; rc = ops->ndo_start_xmit(nskb, dev); - trace_net_dev_xmit(nskb, rc); + trace_net_dev_xmit(nskb, rc, dev, skb_len); if (unlikely(rc != NETDEV_TX_OK)) { if (rc & ~NETDEV_TX_MASK) goto out_kfree_gso_skb; @@ -2502,8 +2515,8 @@ static inline void ____napi_schedule(struct softnet_data *sd, __u32 __skb_get_rxhash(struct sk_buff *skb) { int nhoff, hash = 0, poff; - struct ipv6hdr *ip6; - struct iphdr *ip; + const struct ipv6hdr *ip6; + const struct iphdr *ip; u8 ip_proto; u32 addr1, addr2, ihl; union { @@ -2518,7 +2531,7 @@ __u32 __skb_get_rxhash(struct sk_buff *skb) if (!pskb_may_pull(skb, sizeof(*ip) + nhoff)) goto done; - ip = (struct iphdr *) (skb->data + nhoff); + ip = (const struct iphdr *) (skb->data + nhoff); if (ip->frag_off & htons(IP_MF | IP_OFFSET)) ip_proto = 0; else @@ -2531,7 +2544,7 @@ __u32 __skb_get_rxhash(struct sk_buff *skb) if (!pskb_may_pull(skb, sizeof(*ip6) + nhoff)) goto done; - ip6 = (struct ipv6hdr *) (skb->data + nhoff); + ip6 = (const struct ipv6hdr *) (skb->data + nhoff); ip_proto = ip6->nexthdr; addr1 = (__force u32) ip6->saddr.s6_addr32[3]; addr2 = (__force u32) ip6->daddr.s6_addr32[3]; @@ -3076,25 +3089,6 @@ void netdev_rx_handler_unregister(struct net_device *dev) } EXPORT_SYMBOL_GPL(netdev_rx_handler_unregister); -static void vlan_on_bond_hook(struct sk_buff *skb) -{ - /* - * Make sure ARP frames received on VLAN interfaces stacked on - * bonding interfaces still make their way to any base bonding - * device that may have registered for a specific ptype. - */ - if (skb->dev->priv_flags & IFF_802_1Q_VLAN && - vlan_dev_real_dev(skb->dev)->priv_flags & IFF_BONDING && - skb->protocol == htons(ETH_P_ARP)) { - struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); - - if (!skb2) - return; - skb2->dev = vlan_dev_real_dev(skb->dev); - netif_rx(skb2); - } -} - static int __netif_receive_skb(struct sk_buff *skb) { struct packet_type *ptype, *pt_prev; @@ -3190,8 +3184,6 @@ ncls: goto out; } - vlan_on_bond_hook(skb); - /* deliver only exact match when indicated */ null_or_dev = deliver_exact ? skb->dev : NULL; @@ -4312,10 +4304,8 @@ int netdev_set_master(struct net_device *slave, struct net_device *master) slave->master = master; - if (old) { - synchronize_net(); + if (old) dev_put(old); - } return 0; } EXPORT_SYMBOL(netdev_set_master); @@ -4516,6 +4506,30 @@ void dev_set_rx_mode(struct net_device *dev) } /** + * dev_ethtool_get_settings - call device's ethtool_ops::get_settings() + * @dev: device + * @cmd: memory area for ethtool_ops::get_settings() result + * + * The cmd arg is initialized properly (cleared and + * ethtool_cmd::cmd field set to ETHTOOL_GSET). + * + * Return device's ethtool_ops::get_settings() result value or + * -EOPNOTSUPP when device doesn't expose + * ethtool_ops::get_settings() operation. + */ +int dev_ethtool_get_settings(struct net_device *dev, + struct ethtool_cmd *cmd) +{ + if (!dev->ethtool_ops || !dev->ethtool_ops->get_settings) + return -EOPNOTSUPP; + + memset(cmd, 0, sizeof(struct ethtool_cmd)); + cmd->cmd = ETHTOOL_GSET; + return dev->ethtool_ops->get_settings(dev, cmd); +} +EXPORT_SYMBOL(dev_ethtool_get_settings); + +/** * dev_get_flags - get flags reported to userspace * @dev: device * @@ -4779,7 +4793,7 @@ static int dev_ifsioc_locked(struct net *net, struct ifreq *ifr, unsigned int cm * is never reached */ WARN_ON(1); - err = -EINVAL; + err = -ENOTTY; break; } @@ -5047,7 +5061,7 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg) /* Set the per device memory buffer space. * Not applicable in our case */ case SIOCSIFLINK: - return -EINVAL; + return -ENOTTY; /* * Unknown or private ioctl. @@ -5068,7 +5082,7 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg) /* Take care of Wireless Extensions */ if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) return wext_handle_ioctl(net, &ifr, cmd, arg); - return -EINVAL; + return -ENOTTY; } } @@ -5120,7 +5134,7 @@ static void rollback_registered_many(struct list_head *head) list_del(&dev->unreg_list); continue; } - + dev->dismantle = true; BUG_ON(dev->reg_state != NETREG_REGISTERED); } @@ -5190,27 +5204,27 @@ u32 netdev_fix_features(struct net_device *dev, u32 features) /* Fix illegal checksum combinations */ if ((features & NETIF_F_HW_CSUM) && (features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) { - netdev_info(dev, "mixed HW and IP checksum settings.\n"); + netdev_warn(dev, "mixed HW and IP checksum settings.\n"); features &= ~(NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM); } if ((features & NETIF_F_NO_CSUM) && (features & (NETIF_F_HW_CSUM|NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) { - netdev_info(dev, "mixed no checksumming and other settings.\n"); + netdev_warn(dev, "mixed no checksumming and other settings.\n"); features &= ~(NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM|NETIF_F_HW_CSUM); } /* Fix illegal SG+CSUM combinations. */ if ((features & NETIF_F_SG) && !(features & NETIF_F_ALL_CSUM)) { - netdev_info(dev, - "Dropping NETIF_F_SG since no checksum feature.\n"); + netdev_dbg(dev, + "Dropping NETIF_F_SG since no checksum feature.\n"); features &= ~NETIF_F_SG; } /* TSO requires that SG is present as well. */ if ((features & NETIF_F_ALL_TSO) && !(features & NETIF_F_SG)) { - netdev_info(dev, "Dropping TSO features since no SG feature.\n"); + netdev_dbg(dev, "Dropping TSO features since no SG feature.\n"); features &= ~NETIF_F_ALL_TSO; } @@ -5220,7 +5234,7 @@ u32 netdev_fix_features(struct net_device *dev, u32 features) /* Software GSO depends on SG. */ if ((features & NETIF_F_GSO) && !(features & NETIF_F_SG)) { - netdev_info(dev, "Dropping NETIF_F_GSO since no SG feature.\n"); + netdev_dbg(dev, "Dropping NETIF_F_GSO since no SG feature.\n"); features &= ~NETIF_F_GSO; } @@ -5230,13 +5244,13 @@ u32 netdev_fix_features(struct net_device *dev, u32 features) if (!((features & NETIF_F_GEN_CSUM) || (features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM)) == (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) { - netdev_info(dev, + netdev_dbg(dev, "Dropping NETIF_F_UFO since no checksum offload features.\n"); features &= ~NETIF_F_UFO; } if (!(features & NETIF_F_SG)) { - netdev_info(dev, + netdev_dbg(dev, "Dropping NETIF_F_UFO since no NETIF_F_SG feature.\n"); features &= ~NETIF_F_UFO; } @@ -5264,7 +5278,7 @@ int __netdev_update_features(struct net_device *dev) if (dev->features == features) return 0; - netdev_info(dev, "Features changed: 0x%08x -> 0x%08x\n", + netdev_dbg(dev, "Features changed: 0x%08x -> 0x%08x\n", dev->features, features); if (dev->netdev_ops->ndo_set_features) @@ -5283,6 +5297,14 @@ int __netdev_update_features(struct net_device *dev) return 1; } +/** + * netdev_update_features - recalculate device features + * @dev: the device to check + * + * Recalculate dev->features set and send notifications if it + * has changed. Should be called after driver or hardware dependent + * conditions might have changed that influence the features. + */ void netdev_update_features(struct net_device *dev) { if (__netdev_update_features(dev)) @@ -5291,6 +5313,23 @@ void netdev_update_features(struct net_device *dev) EXPORT_SYMBOL(netdev_update_features); /** + * netdev_change_features - recalculate device features + * @dev: the device to check + * + * Recalculate dev->features set and send notifications even + * if they have not changed. Should be called instead of + * netdev_update_features() if also dev->vlan_features might + * have changed to allow the changes to be propagated to stacked + * VLAN devices. + */ +void netdev_change_features(struct net_device *dev) +{ + __netdev_update_features(dev); + netdev_features_change(dev); +} +EXPORT_SYMBOL(netdev_change_features); + +/** * netif_stacked_transfer_operstate - transfer operstate * @rootdev: the root or lower level device to transfer state from * @dev: the device to transfer operstate to @@ -5406,6 +5445,10 @@ int register_netdevice(struct net_device *dev) dev->iflink = -1; + ret = dev_get_valid_name(dev, dev->name); + if (ret < 0) + goto out; + /* Init, if this function is available */ if (dev->netdev_ops->ndo_init) { ret = dev->netdev_ops->ndo_init(dev); @@ -5416,10 +5459,6 @@ int register_netdevice(struct net_device *dev) } } - ret = dev_get_valid_name(dev, dev->name, 0); - if (ret) - goto err_uninit; - dev->ifindex = dev_new_index(net); if (dev->iflink == -1) dev->iflink = dev->ifindex; @@ -5431,12 +5470,6 @@ int register_netdevice(struct net_device *dev) dev->features |= NETIF_F_SOFT_FEATURES; dev->wanted_features = dev->features & dev->hw_features; - /* Avoid warning from netdev_fix_features() for GSO without SG */ - if (!(dev->wanted_features & NETIF_F_SG)) { - dev->wanted_features &= ~NETIF_F_GSO; - dev->features &= ~NETIF_F_GSO; - } - /* Turn on no cache copy if HW is doing checksum */ dev->hw_features |= NETIF_F_NOCACHE_COPY; if ((dev->features & NETIF_F_ALL_CSUM) && @@ -5558,19 +5591,7 @@ int register_netdev(struct net_device *dev) int err; rtnl_lock(); - - /* - * If the name is a format string the caller wants us to do a - * name allocation. - */ - if (strchr(dev->name, '%')) { - err = dev_alloc_name(dev, dev->name); - if (err < 0) - goto out; - } - err = register_netdevice(dev); -out: rtnl_unlock(); return err; } @@ -5846,8 +5867,6 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, dev->gso_max_size = GSO_MAX_SIZE; - INIT_LIST_HEAD(&dev->ethtool_ntuple_list.list); - dev->ethtool_ntuple_list.count = 0; INIT_LIST_HEAD(&dev->napi_list); INIT_LIST_HEAD(&dev->unreg_list); INIT_LIST_HEAD(&dev->link_watch_list); @@ -5911,9 +5930,6 @@ void free_netdev(struct net_device *dev) /* Flush device addresses */ dev_addr_flush(dev); - /* Clear ethtool n-tuple list */ - ethtool_ntuple_flush(dev); - list_for_each_entry_safe(p, n, &dev->napi_list, dev_list) netif_napi_del(p); @@ -5943,7 +5959,10 @@ EXPORT_SYMBOL(free_netdev); void synchronize_net(void) { might_sleep(); - synchronize_rcu(); + if (rtnl_is_locked()) + synchronize_rcu_expedited(); + else + synchronize_rcu(); } EXPORT_SYMBOL(synchronize_net); @@ -6052,7 +6071,7 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char /* We get here if we can't use the current device name */ if (!pat) goto out; - if (dev_get_valid_name(dev, pat, 1)) + if (dev_get_valid_name(dev, pat) < 0) goto out; } @@ -6184,33 +6203,20 @@ static int dev_cpu_callback(struct notifier_block *nfb, */ u32 netdev_increment_features(u32 all, u32 one, u32 mask) { - /* If device needs checksumming, downgrade to it. */ - if (all & NETIF_F_NO_CSUM && !(one & NETIF_F_NO_CSUM)) - all ^= NETIF_F_NO_CSUM | (one & NETIF_F_ALL_CSUM); - else if (mask & NETIF_F_ALL_CSUM) { - /* If one device supports v4/v6 checksumming, set for all. */ - if (one & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM) && - !(all & NETIF_F_GEN_CSUM)) { - all &= ~NETIF_F_ALL_CSUM; - all |= one & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM); - } + if (mask & NETIF_F_GEN_CSUM) + mask |= NETIF_F_ALL_CSUM; + mask |= NETIF_F_VLAN_CHALLENGED; - /* If one device supports hw checksumming, set for all. */ - if (one & NETIF_F_GEN_CSUM && !(all & NETIF_F_GEN_CSUM)) { - all &= ~NETIF_F_ALL_CSUM; - all |= NETIF_F_HW_CSUM; - } - } + all |= one & (NETIF_F_ONE_FOR_ALL|NETIF_F_ALL_CSUM) & mask; + all &= one | ~NETIF_F_ALL_FOR_ALL; - /* If device can't no cache copy, don't do for all */ - if (!(one & NETIF_F_NOCACHE_COPY)) - all &= ~NETIF_F_NOCACHE_COPY; - - one |= NETIF_F_ALL_CSUM; + /* If device needs checksumming, downgrade to it. */ + if (all & (NETIF_F_ALL_CSUM & ~NETIF_F_NO_CSUM)) + all &= ~NETIF_F_NO_CSUM; - one |= all & NETIF_F_ONE_FOR_ALL; - all &= one | NETIF_F_LLTX | NETIF_F_GSO | NETIF_F_UFO; - all |= one & mask & NETIF_F_ONE_FOR_ALL; + /* If one device supports hw checksumming, set for all. */ + if (all & NETIF_F_GEN_CSUM) + all &= ~(NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM); return all; } diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c index 7b39f3ed2fd..e2e66939ed0 100644 --- a/net/core/dev_addr_lists.c +++ b/net/core/dev_addr_lists.c @@ -68,14 +68,6 @@ static int __hw_addr_add(struct netdev_hw_addr_list *list, unsigned char *addr, return __hw_addr_add_ex(list, addr, addr_len, addr_type, false); } -static void ha_rcu_free(struct rcu_head *head) -{ - struct netdev_hw_addr *ha; - - ha = container_of(head, struct netdev_hw_addr, rcu_head); - kfree(ha); -} - static int __hw_addr_del_ex(struct netdev_hw_addr_list *list, unsigned char *addr, int addr_len, unsigned char addr_type, bool global) @@ -94,7 +86,7 @@ static int __hw_addr_del_ex(struct netdev_hw_addr_list *list, if (--ha->refcount) return 0; list_del_rcu(&ha->list); - call_rcu(&ha->rcu_head, ha_rcu_free); + kfree_rcu(ha, rcu_head); list->count--; return 0; } @@ -197,7 +189,7 @@ void __hw_addr_flush(struct netdev_hw_addr_list *list) list_for_each_entry_safe(ha, tmp, &list->list, list) { list_del_rcu(&ha->list); - call_rcu(&ha->rcu_head, ha_rcu_free); + kfree_rcu(ha, rcu_head); } list->count = 0; } diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c index 706502ff64a..7f36b38e060 100644 --- a/net/core/drop_monitor.c +++ b/net/core/drop_monitor.c @@ -207,14 +207,6 @@ static void trace_napi_poll_hit(void *ignore, struct napi_struct *napi) rcu_read_unlock(); } - -static void free_dm_hw_stat(struct rcu_head *head) -{ - struct dm_hw_stat_delta *n; - n = container_of(head, struct dm_hw_stat_delta, rcu); - kfree(n); -} - static int set_all_monitor_traces(int state) { int rc = 0; @@ -245,7 +237,7 @@ static int set_all_monitor_traces(int state) list_for_each_entry_safe(new_stat, temp, &hw_stats_list, list) { if (new_stat->dev == NULL) { list_del_rcu(&new_stat->list); - call_rcu(&new_stat->rcu, free_dm_hw_stat); + kfree_rcu(new_stat, rcu); } } break; @@ -314,7 +306,7 @@ static int dropmon_net_event(struct notifier_block *ev_block, new_stat->dev = NULL; if (trace_state == TRACE_OFF) { list_del_rcu(&new_stat->list); - call_rcu(&new_stat->rcu, free_dm_hw_stat); + kfree_rcu(new_stat, rcu); break; } } diff --git a/net/core/dst.c b/net/core/dst.c index 91104d35de7..9ccca038444 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -19,6 +19,7 @@ #include <linux/types.h> #include <net/net_namespace.h> #include <linux/sched.h> +#include <linux/prefetch.h> #include <net/dst.h> @@ -33,9 +34,6 @@ * 3) This list is guarded by a mutex, * so that the gc_task and dst_dev_event() can be synchronized. */ -#if RT_CACHE_DEBUG >= 2 -static atomic_t dst_total = ATOMIC_INIT(0); -#endif /* * We want to keep lock & list close together @@ -69,10 +67,6 @@ static void dst_gc_task(struct work_struct *work) unsigned long expires = ~0L; struct dst_entry *dst, *next, head; struct dst_entry *last = &head; -#if RT_CACHE_DEBUG >= 2 - ktime_t time_start = ktime_get(); - struct timespec elapsed; -#endif mutex_lock(&dst_gc_mutex); next = dst_busy_list; @@ -146,15 +140,6 @@ loop: spin_unlock_bh(&dst_garbage.lock); mutex_unlock(&dst_gc_mutex); -#if RT_CACHE_DEBUG >= 2 - elapsed = ktime_to_timespec(ktime_sub(ktime_get(), time_start)); - printk(KERN_DEBUG "dst_total: %d delayed: %d work_perf: %d" - " expires: %lu elapsed: %lu us\n", - atomic_read(&dst_total), delayed, work_performed, - expires, - elapsed.tv_sec * USEC_PER_SEC + - elapsed.tv_nsec / NSEC_PER_USEC); -#endif } int dst_discard(struct sk_buff *skb) @@ -166,7 +151,8 @@ EXPORT_SYMBOL(dst_discard); const u32 dst_default_metrics[RTAX_MAX]; -void *dst_alloc(struct dst_ops *ops, int initial_ref) +void *dst_alloc(struct dst_ops *ops, struct net_device *dev, + int initial_ref, int initial_obsolete, int flags) { struct dst_entry *dst; @@ -174,18 +160,36 @@ void *dst_alloc(struct dst_ops *ops, int initial_ref) if (ops->gc(ops)) return NULL; } - dst = kmem_cache_zalloc(ops->kmem_cachep, GFP_ATOMIC); + dst = kmem_cache_alloc(ops->kmem_cachep, GFP_ATOMIC); if (!dst) return NULL; - atomic_set(&dst->__refcnt, initial_ref); + dst->child = NULL; + dst->dev = dev; + if (dev) + dev_hold(dev); dst->ops = ops; - dst->lastuse = jiffies; - dst->path = dst; - dst->input = dst->output = dst_discard; dst_init_metrics(dst, dst_default_metrics, true); -#if RT_CACHE_DEBUG >= 2 - atomic_inc(&dst_total); + dst->expires = 0UL; + dst->path = dst; + dst->neighbour = NULL; + dst->hh = NULL; +#ifdef CONFIG_XFRM + dst->xfrm = NULL; +#endif + dst->input = dst_discard; + dst->output = dst_discard; + dst->error = 0; + dst->obsolete = initial_obsolete; + dst->header_len = 0; + dst->trailer_len = 0; +#ifdef CONFIG_IP_ROUTE_CLASSID + dst->tclassid = 0; #endif + atomic_set(&dst->__refcnt, initial_ref); + dst->__use = 0; + dst->lastuse = jiffies; + dst->flags = flags; + dst->next = NULL; dst_entries_add(ops, 1); return dst; } @@ -245,9 +249,6 @@ again: dst->ops->destroy(dst); if (dst->dev) dev_put(dst->dev); -#if RT_CACHE_DEBUG >= 2 - atomic_dec(&dst_total); -#endif kmem_cache_free(dst->ops->kmem_cachep, dst); dst = child; @@ -314,7 +315,7 @@ void __dst_destroy_metrics_generic(struct dst_entry *dst, unsigned long old) { unsigned long prev, new; - new = (unsigned long) dst_default_metrics; + new = ((unsigned long) dst_default_metrics) | DST_METRICS_READ_ONLY; prev = cmpxchg(&dst->_metrics, old, new); if (prev == old) kfree(__DST_METRICS_PTR(old)); diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 13d79f5a86e..b7c12a63d0c 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -169,18 +169,6 @@ int ethtool_op_set_flags(struct net_device *dev, u32 data, u32 supported) } EXPORT_SYMBOL(ethtool_op_set_flags); -void ethtool_ntuple_flush(struct net_device *dev) -{ - struct ethtool_rx_ntuple_flow_spec_container *fsc, *f; - - list_for_each_entry_safe(fsc, f, &dev->ethtool_ntuple_list.list, list) { - list_del(&fsc->list); - kfree(fsc); - } - dev->ethtool_ntuple_list.count = 0; -} -EXPORT_SYMBOL(ethtool_ntuple_flush); - /* Handlers for each ethtool command */ #define ETHTOOL_DEV_FEATURE_WORDS 1 @@ -233,6 +221,29 @@ static int ethtool_set_feature_compat(struct net_device *dev, return 1; } +static int ethtool_set_flags_compat(struct net_device *dev, + int (*legacy_set)(struct net_device *, u32), + struct ethtool_set_features_block *features, u32 mask) +{ + u32 value; + + if (!legacy_set) + return 0; + + if (!(features[0].valid & mask)) + return 0; + + value = dev->features & ~features[0].valid; + value |= features[0].requested; + + features[0].valid &= ~mask; + + if (legacy_set(dev, value & mask) < 0) + netdev_info(dev, "Legacy flags change failed\n"); + + return 1; +} + static int ethtool_set_features_compat(struct net_device *dev, struct ethtool_set_features_block *features) { @@ -249,7 +260,7 @@ static int ethtool_set_features_compat(struct net_device *dev, features, NETIF_F_ALL_TSO); compat |= ethtool_set_feature_compat(dev, dev->ethtool_ops->set_rx_csum, features, NETIF_F_RXCSUM); - compat |= ethtool_set_feature_compat(dev, dev->ethtool_ops->set_flags, + compat |= ethtool_set_flags_compat(dev, dev->ethtool_ops->set_flags, features, flags_dup_features); return compat; @@ -332,7 +343,7 @@ static const char netdev_features_strings[ETHTOOL_DEV_FEATURE_WORDS * 32][ETH_GS /* NETIF_F_IP_CSUM */ "tx-checksum-ipv4", /* NETIF_F_NO_CSUM */ "tx-checksum-unneeded", /* NETIF_F_HW_CSUM */ "tx-checksum-ip-generic", - /* NETIF_F_IPV6_CSUM */ "tx_checksum-ipv6", + /* NETIF_F_IPV6_CSUM */ "tx-checksum-ipv6", /* NETIF_F_HIGHDMA */ "highdma", /* NETIF_F_FRAGLIST */ "tx-scatter-gather-fraglist", /* NETIF_F_HW_VLAN_TX */ "tx-vlan-hw-insert", @@ -361,8 +372,8 @@ static const char netdev_features_strings[ETHTOOL_DEV_FEATURE_WORDS * 32][ETH_GS /* NETIF_F_NTUPLE */ "rx-ntuple-filter", /* NETIF_F_RXHASH */ "rx-hashing", /* NETIF_F_RXCSUM */ "rx-checksum", - /* NETIF_F_NOCACHE_COPY */ "tx-nocache-copy" - "", + /* NETIF_F_NOCACHE_COPY */ "tx-nocache-copy", + /* NETIF_F_LOOPBACK */ "loopback", }; static int __ethtool_get_sset_count(struct net_device *dev, int sset) @@ -546,12 +557,12 @@ int __ethtool_set_flags(struct net_device *dev, u32 data) } /* allow changing only bits set in hw_features */ - changed = (data ^ dev->wanted_features) & flags_dup_features; + changed = (data ^ dev->features) & flags_dup_features; if (changed & ~dev->hw_features) return (changed & dev->hw_features) ? -EINVAL : -EOPNOTSUPP; dev->wanted_features = - (dev->wanted_features & ~changed) | data; + (dev->wanted_features & ~changed) | (data & dev->hw_features); __netdev_update_features(dev); @@ -842,34 +853,6 @@ out: return ret; } -static void __rx_ntuple_filter_add(struct ethtool_rx_ntuple_list *list, - struct ethtool_rx_ntuple_flow_spec *spec, - struct ethtool_rx_ntuple_flow_spec_container *fsc) -{ - - /* don't add filters forever */ - if (list->count >= ETHTOOL_MAX_NTUPLE_LIST_ENTRY) { - /* free the container */ - kfree(fsc); - return; - } - - /* Copy the whole filter over */ - fsc->fs.flow_type = spec->flow_type; - memcpy(&fsc->fs.h_u, &spec->h_u, sizeof(spec->h_u)); - memcpy(&fsc->fs.m_u, &spec->m_u, sizeof(spec->m_u)); - - fsc->fs.vlan_tag = spec->vlan_tag; - fsc->fs.vlan_tag_mask = spec->vlan_tag_mask; - fsc->fs.data = spec->data; - fsc->fs.data_mask = spec->data_mask; - fsc->fs.action = spec->action; - - /* add to the list */ - list_add_tail_rcu(&fsc->list, &list->list); - list->count++; -} - /* * ethtool does not (or did not) set masks for flow parameters that are * not specified, so if both value and mask are 0 then this must be @@ -907,8 +890,6 @@ static noinline_for_stack int ethtool_set_rx_ntuple(struct net_device *dev, { struct ethtool_rx_ntuple cmd; const struct ethtool_ops *ops = dev->ethtool_ops; - struct ethtool_rx_ntuple_flow_spec_container *fsc = NULL; - int ret; if (!ops->set_rx_ntuple) return -EOPNOTSUPP; @@ -921,269 +902,7 @@ static noinline_for_stack int ethtool_set_rx_ntuple(struct net_device *dev, rx_ntuple_fix_masks(&cmd.fs); - /* - * Cache filter in dev struct for GET operation only if - * the underlying driver doesn't have its own GET operation, and - * only if the filter was added successfully. First make sure we - * can allocate the filter, then continue if successful. - */ - if (!ops->get_rx_ntuple) { - fsc = kmalloc(sizeof(*fsc), GFP_ATOMIC); - if (!fsc) - return -ENOMEM; - } - - ret = ops->set_rx_ntuple(dev, &cmd); - if (ret) { - kfree(fsc); - return ret; - } - - if (!ops->get_rx_ntuple) - __rx_ntuple_filter_add(&dev->ethtool_ntuple_list, &cmd.fs, fsc); - - return ret; -} - -static int ethtool_get_rx_ntuple(struct net_device *dev, void __user *useraddr) -{ - struct ethtool_gstrings gstrings; - const struct ethtool_ops *ops = dev->ethtool_ops; - struct ethtool_rx_ntuple_flow_spec_container *fsc; - u8 *data; - char *p; - int ret, i, num_strings = 0; - - if (!ops->get_sset_count) - return -EOPNOTSUPP; - - if (copy_from_user(&gstrings, useraddr, sizeof(gstrings))) - return -EFAULT; - - ret = ops->get_sset_count(dev, gstrings.string_set); - if (ret < 0) - return ret; - - gstrings.len = ret; - - data = kzalloc(gstrings.len * ETH_GSTRING_LEN, GFP_USER); - if (!data) - return -ENOMEM; - - if (ops->get_rx_ntuple) { - /* driver-specific filter grab */ - ret = ops->get_rx_ntuple(dev, gstrings.string_set, data); - goto copy; - } - - /* default ethtool filter grab */ - i = 0; - p = (char *)data; - list_for_each_entry(fsc, &dev->ethtool_ntuple_list.list, list) { - sprintf(p, "Filter %d:\n", i); - p += ETH_GSTRING_LEN; - num_strings++; - - switch (fsc->fs.flow_type) { - case TCP_V4_FLOW: - sprintf(p, "\tFlow Type: TCP\n"); - p += ETH_GSTRING_LEN; - num_strings++; - break; - case UDP_V4_FLOW: - sprintf(p, "\tFlow Type: UDP\n"); - p += ETH_GSTRING_LEN; - num_strings++; - break; - case SCTP_V4_FLOW: - sprintf(p, "\tFlow Type: SCTP\n"); - p += ETH_GSTRING_LEN; - num_strings++; - break; - case AH_ESP_V4_FLOW: - sprintf(p, "\tFlow Type: AH ESP\n"); - p += ETH_GSTRING_LEN; - num_strings++; - break; - case ESP_V4_FLOW: - sprintf(p, "\tFlow Type: ESP\n"); - p += ETH_GSTRING_LEN; - num_strings++; - break; - case IP_USER_FLOW: - sprintf(p, "\tFlow Type: Raw IP\n"); - p += ETH_GSTRING_LEN; - num_strings++; - break; - case IPV4_FLOW: - sprintf(p, "\tFlow Type: IPv4\n"); - p += ETH_GSTRING_LEN; - num_strings++; - break; - default: - sprintf(p, "\tFlow Type: Unknown\n"); - p += ETH_GSTRING_LEN; - num_strings++; - goto unknown_filter; - } - - /* now the rest of the filters */ - switch (fsc->fs.flow_type) { - case TCP_V4_FLOW: - case UDP_V4_FLOW: - case SCTP_V4_FLOW: - sprintf(p, "\tSrc IP addr: 0x%x\n", - fsc->fs.h_u.tcp_ip4_spec.ip4src); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tSrc IP mask: 0x%x\n", - fsc->fs.m_u.tcp_ip4_spec.ip4src); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tDest IP addr: 0x%x\n", - fsc->fs.h_u.tcp_ip4_spec.ip4dst); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tDest IP mask: 0x%x\n", - fsc->fs.m_u.tcp_ip4_spec.ip4dst); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tSrc Port: %d, mask: 0x%x\n", - fsc->fs.h_u.tcp_ip4_spec.psrc, - fsc->fs.m_u.tcp_ip4_spec.psrc); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tDest Port: %d, mask: 0x%x\n", - fsc->fs.h_u.tcp_ip4_spec.pdst, - fsc->fs.m_u.tcp_ip4_spec.pdst); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tTOS: %d, mask: 0x%x\n", - fsc->fs.h_u.tcp_ip4_spec.tos, - fsc->fs.m_u.tcp_ip4_spec.tos); - p += ETH_GSTRING_LEN; - num_strings++; - break; - case AH_ESP_V4_FLOW: - case ESP_V4_FLOW: - sprintf(p, "\tSrc IP addr: 0x%x\n", - fsc->fs.h_u.ah_ip4_spec.ip4src); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tSrc IP mask: 0x%x\n", - fsc->fs.m_u.ah_ip4_spec.ip4src); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tDest IP addr: 0x%x\n", - fsc->fs.h_u.ah_ip4_spec.ip4dst); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tDest IP mask: 0x%x\n", - fsc->fs.m_u.ah_ip4_spec.ip4dst); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tSPI: %d, mask: 0x%x\n", - fsc->fs.h_u.ah_ip4_spec.spi, - fsc->fs.m_u.ah_ip4_spec.spi); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tTOS: %d, mask: 0x%x\n", - fsc->fs.h_u.ah_ip4_spec.tos, - fsc->fs.m_u.ah_ip4_spec.tos); - p += ETH_GSTRING_LEN; - num_strings++; - break; - case IP_USER_FLOW: - sprintf(p, "\tSrc IP addr: 0x%x\n", - fsc->fs.h_u.usr_ip4_spec.ip4src); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tSrc IP mask: 0x%x\n", - fsc->fs.m_u.usr_ip4_spec.ip4src); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tDest IP addr: 0x%x\n", - fsc->fs.h_u.usr_ip4_spec.ip4dst); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tDest IP mask: 0x%x\n", - fsc->fs.m_u.usr_ip4_spec.ip4dst); - p += ETH_GSTRING_LEN; - num_strings++; - break; - case IPV4_FLOW: - sprintf(p, "\tSrc IP addr: 0x%x\n", - fsc->fs.h_u.usr_ip4_spec.ip4src); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tSrc IP mask: 0x%x\n", - fsc->fs.m_u.usr_ip4_spec.ip4src); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tDest IP addr: 0x%x\n", - fsc->fs.h_u.usr_ip4_spec.ip4dst); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tDest IP mask: 0x%x\n", - fsc->fs.m_u.usr_ip4_spec.ip4dst); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tL4 bytes: 0x%x, mask: 0x%x\n", - fsc->fs.h_u.usr_ip4_spec.l4_4_bytes, - fsc->fs.m_u.usr_ip4_spec.l4_4_bytes); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tTOS: %d, mask: 0x%x\n", - fsc->fs.h_u.usr_ip4_spec.tos, - fsc->fs.m_u.usr_ip4_spec.tos); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tIP Version: %d, mask: 0x%x\n", - fsc->fs.h_u.usr_ip4_spec.ip_ver, - fsc->fs.m_u.usr_ip4_spec.ip_ver); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tProtocol: %d, mask: 0x%x\n", - fsc->fs.h_u.usr_ip4_spec.proto, - fsc->fs.m_u.usr_ip4_spec.proto); - p += ETH_GSTRING_LEN; - num_strings++; - break; - } - sprintf(p, "\tVLAN: %d, mask: 0x%x\n", - fsc->fs.vlan_tag, fsc->fs.vlan_tag_mask); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tUser-defined: 0x%Lx\n", fsc->fs.data); - p += ETH_GSTRING_LEN; - num_strings++; - sprintf(p, "\tUser-defined mask: 0x%Lx\n", fsc->fs.data_mask); - p += ETH_GSTRING_LEN; - num_strings++; - if (fsc->fs.action == ETHTOOL_RXNTUPLE_ACTION_DROP) - sprintf(p, "\tAction: Drop\n"); - else - sprintf(p, "\tAction: Direct to queue %d\n", - fsc->fs.action); - p += ETH_GSTRING_LEN; - num_strings++; -unknown_filter: - i++; - } -copy: - /* indicate to userspace how many strings we actually have */ - gstrings.len = num_strings; - ret = -EFAULT; - if (copy_to_user(useraddr, &gstrings, sizeof(gstrings))) - goto out; - useraddr += sizeof(gstrings); - if (copy_to_user(useraddr, data, gstrings.len * ETH_GSTRING_LEN)) - goto out; - ret = 0; - -out: - kfree(data); - return ret; + return ops->set_rx_ntuple(dev, &cmd); } static int ethtool_get_regs(struct net_device *dev, char __user *useraddr) @@ -1655,7 +1374,7 @@ static int ethtool_phys_id(struct net_device *dev, void __user *useraddr) static bool busy; int rc; - if (!dev->ethtool_ops->set_phys_id && !dev->ethtool_ops->phys_id) + if (!dev->ethtool_ops->set_phys_id) return -EOPNOTSUPP; if (busy) @@ -1664,10 +1383,6 @@ static int ethtool_phys_id(struct net_device *dev, void __user *useraddr) if (copy_from_user(&id, useraddr, sizeof(id))) return -EFAULT; - if (!dev->ethtool_ops->set_phys_id) - /* Do it the old way */ - return dev->ethtool_ops->phys_id(dev, id.data); - rc = dev->ethtool_ops->set_phys_id(dev, ETHTOOL_ID_ACTIVE); if (rc < 0) return rc; @@ -1827,6 +1542,87 @@ static noinline_for_stack int ethtool_flash_device(struct net_device *dev, return dev->ethtool_ops->flash_device(dev, &efl); } +static int ethtool_set_dump(struct net_device *dev, + void __user *useraddr) +{ + struct ethtool_dump dump; + + if (!dev->ethtool_ops->set_dump) + return -EOPNOTSUPP; + + if (copy_from_user(&dump, useraddr, sizeof(dump))) + return -EFAULT; + + return dev->ethtool_ops->set_dump(dev, &dump); +} + +static int ethtool_get_dump_flag(struct net_device *dev, + void __user *useraddr) +{ + int ret; + struct ethtool_dump dump; + const struct ethtool_ops *ops = dev->ethtool_ops; + + if (!dev->ethtool_ops->get_dump_flag) + return -EOPNOTSUPP; + + if (copy_from_user(&dump, useraddr, sizeof(dump))) + return -EFAULT; + + ret = ops->get_dump_flag(dev, &dump); + if (ret) + return ret; + + if (copy_to_user(useraddr, &dump, sizeof(dump))) + return -EFAULT; + return 0; +} + +static int ethtool_get_dump_data(struct net_device *dev, + void __user *useraddr) +{ + int ret; + __u32 len; + struct ethtool_dump dump, tmp; + const struct ethtool_ops *ops = dev->ethtool_ops; + void *data = NULL; + + if (!dev->ethtool_ops->get_dump_data || + !dev->ethtool_ops->get_dump_flag) + return -EOPNOTSUPP; + + if (copy_from_user(&dump, useraddr, sizeof(dump))) + return -EFAULT; + + memset(&tmp, 0, sizeof(tmp)); + tmp.cmd = ETHTOOL_GET_DUMP_FLAG; + ret = ops->get_dump_flag(dev, &tmp); + if (ret) + return ret; + + len = (tmp.len > dump.len) ? dump.len : tmp.len; + if (!len) + return -EFAULT; + + data = vzalloc(tmp.len); + if (!data) + return -ENOMEM; + ret = ops->get_dump_data(dev, &dump, data); + if (ret) + goto out; + + if (copy_to_user(useraddr, &dump, sizeof(dump))) { + ret = -EFAULT; + goto out; + } + useraddr += offsetof(struct ethtool_dump, data); + if (copy_to_user(useraddr, data, len)) + ret = -EFAULT; +out: + vfree(data); + return ret; +} + /* The main entry point in this file. Called from net/core/dev.c */ int dev_ethtool(struct net *net, struct ifreq *ifr) @@ -2001,9 +1797,6 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_SRXNTUPLE: rc = ethtool_set_rx_ntuple(dev, useraddr); break; - case ETHTOOL_GRXNTUPLE: - rc = ethtool_get_rx_ntuple(dev, useraddr); - break; case ETHTOOL_GSSET_INFO: rc = ethtool_get_sset_info(dev, useraddr); break; @@ -2043,6 +1836,15 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_SCHANNELS: rc = ethtool_set_channels(dev, useraddr); break; + case ETHTOOL_SET_DUMP: + rc = ethtool_set_dump(dev, useraddr); + break; + case ETHTOOL_GET_DUMP_FLAG: + rc = ethtool_get_dump_flag(dev, useraddr); + break; + case ETHTOOL_GET_DUMP_DATA: + rc = ethtool_get_dump_data(dev, useraddr); + break; default: rc = -EOPNOTSUPP; } diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 8248ebb5891..e7ab0c0285b 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -590,7 +590,8 @@ static int dump_rules(struct sk_buff *skb, struct netlink_callback *cb, int idx = 0; struct fib_rule *rule; - list_for_each_entry(rule, &ops->rules_list, list) { + rcu_read_lock(); + list_for_each_entry_rcu(rule, &ops->rules_list, list) { if (idx < cb->args[1]) goto skip; @@ -601,6 +602,7 @@ static int dump_rules(struct sk_buff *skb, struct netlink_callback *cb, skip: idx++; } + rcu_read_unlock(); cb->args[1] = idx; rules_ops_put(ops); @@ -738,9 +740,9 @@ static struct pernet_operations fib_rules_net_ops = { static int __init fib_rules_init(void) { int err; - rtnl_register(PF_UNSPEC, RTM_NEWRULE, fib_nl_newrule, NULL); - rtnl_register(PF_UNSPEC, RTM_DELRULE, fib_nl_delrule, NULL); - rtnl_register(PF_UNSPEC, RTM_GETRULE, NULL, fib_nl_dumprule); + rtnl_register(PF_UNSPEC, RTM_NEWRULE, fib_nl_newrule, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_DELRULE, fib_nl_delrule, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_GETRULE, NULL, fib_nl_dumprule, NULL); err = register_pernet_subsys(&fib_rules_net_ops); if (err < 0) diff --git a/net/core/filter.c b/net/core/filter.c index afb8afb066b..36f975fa87c 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -38,65 +38,7 @@ #include <asm/unaligned.h> #include <linux/filter.h> #include <linux/reciprocal_div.h> - -enum { - BPF_S_RET_K = 1, - BPF_S_RET_A, - BPF_S_ALU_ADD_K, - BPF_S_ALU_ADD_X, - BPF_S_ALU_SUB_K, - BPF_S_ALU_SUB_X, - BPF_S_ALU_MUL_K, - BPF_S_ALU_MUL_X, - BPF_S_ALU_DIV_X, - BPF_S_ALU_AND_K, - BPF_S_ALU_AND_X, - BPF_S_ALU_OR_K, - BPF_S_ALU_OR_X, - BPF_S_ALU_LSH_K, - BPF_S_ALU_LSH_X, - BPF_S_ALU_RSH_K, - BPF_S_ALU_RSH_X, - BPF_S_ALU_NEG, - BPF_S_LD_W_ABS, - BPF_S_LD_H_ABS, - BPF_S_LD_B_ABS, - BPF_S_LD_W_LEN, - BPF_S_LD_W_IND, - BPF_S_LD_H_IND, - BPF_S_LD_B_IND, - BPF_S_LD_IMM, - BPF_S_LDX_W_LEN, - BPF_S_LDX_B_MSH, - BPF_S_LDX_IMM, - BPF_S_MISC_TAX, - BPF_S_MISC_TXA, - BPF_S_ALU_DIV_K, - BPF_S_LD_MEM, - BPF_S_LDX_MEM, - BPF_S_ST, - BPF_S_STX, - BPF_S_JMP_JA, - BPF_S_JMP_JEQ_K, - BPF_S_JMP_JEQ_X, - BPF_S_JMP_JGE_K, - BPF_S_JMP_JGE_X, - BPF_S_JMP_JGT_K, - BPF_S_JMP_JGT_X, - BPF_S_JMP_JSET_K, - BPF_S_JMP_JSET_X, - /* Ancillary data */ - BPF_S_ANC_PROTOCOL, - BPF_S_ANC_PKTTYPE, - BPF_S_ANC_IFINDEX, - BPF_S_ANC_NLATTR, - BPF_S_ANC_NLATTR_NEST, - BPF_S_ANC_MARK, - BPF_S_ANC_QUEUE, - BPF_S_ANC_HATYPE, - BPF_S_ANC_RXHASH, - BPF_S_ANC_CPU, -}; +#include <linux/ratelimit.h> /* No hurry in this branch */ static void *__load_pointer(const struct sk_buff *skb, int k, unsigned int size) @@ -145,7 +87,7 @@ int sk_filter(struct sock *sk, struct sk_buff *skb) rcu_read_lock(); filter = rcu_dereference(sk->sk_filter); if (filter) { - unsigned int pkt_len = sk_run_filter(skb, filter->insns); + unsigned int pkt_len = SK_RUN_FILTER(filter, skb); err = pkt_len ? pskb_trim(skb, pkt_len) : -EPERM; } @@ -409,7 +351,9 @@ load_b: continue; } default: - WARN_ON(1); + WARN_RATELIMIT(1, "Unknown code:%u jt:%u tf:%u k:%u\n", + fentry->code, fentry->jt, + fentry->jf, fentry->k); return 0; } } @@ -638,6 +582,7 @@ void sk_filter_release_rcu(struct rcu_head *rcu) { struct sk_filter *fp = container_of(rcu, struct sk_filter, rcu); + bpf_jit_free(fp); kfree(fp); } EXPORT_SYMBOL(sk_filter_release_rcu); @@ -672,6 +617,7 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk) atomic_set(&fp->refcnt, 1); fp->len = fprog->len; + fp->bpf_func = sk_run_filter; err = sk_chk_filter(fp->insns, fp->len); if (err) { @@ -679,6 +625,8 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk) return err; } + bpf_jit_compile(fp); + old_fp = rcu_dereference_protected(sk->sk_filter, sock_owned_by_user(sk)); rcu_assign_pointer(sk->sk_filter, fp); diff --git a/net/core/gen_estimator.c b/net/core/gen_estimator.c index 7c2373321b7..43b03dd71e8 100644 --- a/net/core/gen_estimator.c +++ b/net/core/gen_estimator.c @@ -249,13 +249,6 @@ int gen_new_estimator(struct gnet_stats_basic_packed *bstats, } EXPORT_SYMBOL(gen_new_estimator); -static void __gen_kill_estimator(struct rcu_head *head) -{ - struct gen_estimator *e = container_of(head, - struct gen_estimator, e_rcu); - kfree(e); -} - /** * gen_kill_estimator - remove a rate estimator * @bstats: basic statistics @@ -279,7 +272,7 @@ void gen_kill_estimator(struct gnet_stats_basic_packed *bstats, write_unlock(&est_lock); list_del_rcu(&e->list); - call_rcu(&e->e_rcu, __gen_kill_estimator); + kfree_rcu(e, e_rcu); } spin_unlock_bh(&est_tree_lock); } diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 799f06e03a2..ceb505b1507 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -2909,12 +2909,13 @@ EXPORT_SYMBOL(neigh_sysctl_unregister); static int __init neigh_init(void) { - rtnl_register(PF_UNSPEC, RTM_NEWNEIGH, neigh_add, NULL); - rtnl_register(PF_UNSPEC, RTM_DELNEIGH, neigh_delete, NULL); - rtnl_register(PF_UNSPEC, RTM_GETNEIGH, NULL, neigh_dump_info); + rtnl_register(PF_UNSPEC, RTM_NEWNEIGH, neigh_add, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_DELNEIGH, neigh_delete, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_GETNEIGH, NULL, neigh_dump_info, NULL); - rtnl_register(PF_UNSPEC, RTM_GETNEIGHTBL, NULL, neightbl_dump_info); - rtnl_register(PF_UNSPEC, RTM_SETNEIGHTBL, neightbl_set, NULL); + rtnl_register(PF_UNSPEC, RTM_GETNEIGHTBL, NULL, neightbl_dump_info, + NULL); + rtnl_register(PF_UNSPEC, RTM_SETNEIGHTBL, neightbl_set, NULL, NULL); return 0; } diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 5ceb257e860..11b98bc2aa8 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -28,6 +28,7 @@ static const char fmt_hex[] = "%#x\n"; static const char fmt_long_hex[] = "%#lx\n"; static const char fmt_dec[] = "%d\n"; +static const char fmt_udec[] = "%u\n"; static const char fmt_ulong[] = "%lu\n"; static const char fmt_u64[] = "%llu\n"; @@ -145,13 +146,10 @@ static ssize_t show_speed(struct device *dev, if (!rtnl_trylock()) return restart_syscall(); - if (netif_running(netdev) && - netdev->ethtool_ops && - netdev->ethtool_ops->get_settings) { - struct ethtool_cmd cmd = { ETHTOOL_GSET }; - - if (!netdev->ethtool_ops->get_settings(netdev, &cmd)) - ret = sprintf(buf, fmt_dec, ethtool_cmd_speed(&cmd)); + if (netif_running(netdev)) { + struct ethtool_cmd cmd; + if (!dev_ethtool_get_settings(netdev, &cmd)) + ret = sprintf(buf, fmt_udec, ethtool_cmd_speed(&cmd)); } rtnl_unlock(); return ret; @@ -166,13 +164,11 @@ static ssize_t show_duplex(struct device *dev, if (!rtnl_trylock()) return restart_syscall(); - if (netif_running(netdev) && - netdev->ethtool_ops && - netdev->ethtool_ops->get_settings) { - struct ethtool_cmd cmd = { ETHTOOL_GSET }; - - if (!netdev->ethtool_ops->get_settings(netdev, &cmd)) - ret = sprintf(buf, "%s\n", cmd.duplex ? "full" : "half"); + if (netif_running(netdev)) { + struct ethtool_cmd cmd; + if (!dev_ethtool_get_settings(netdev, &cmd)) + ret = sprintf(buf, "%s\n", + cmd.duplex ? "full" : "half"); } rtnl_unlock(); return ret; @@ -565,13 +561,6 @@ static ssize_t show_rps_map(struct netdev_rx_queue *queue, return len; } -static void rps_map_release(struct rcu_head *rcu) -{ - struct rps_map *map = container_of(rcu, struct rps_map, rcu); - - kfree(map); -} - static ssize_t store_rps_map(struct netdev_rx_queue *queue, struct rx_queue_attribute *attribute, const char *buf, size_t len) @@ -619,7 +608,7 @@ static ssize_t store_rps_map(struct netdev_rx_queue *queue, spin_unlock(&rps_map_lock); if (old_map) - call_rcu(&old_map->rcu, rps_map_release); + kfree_rcu(old_map, rcu); free_cpumask_var(mask); return len; @@ -728,7 +717,7 @@ static void rx_queue_release(struct kobject *kobj) map = rcu_dereference_raw(queue->rps_map); if (map) { RCU_INIT_POINTER(queue->rps_map, NULL); - call_rcu(&map->rcu, rps_map_release); + kfree_rcu(map, rcu); } flow_table = rcu_dereference_raw(queue->rps_flow_table); @@ -898,21 +887,6 @@ static ssize_t show_xps_map(struct netdev_queue *queue, return len; } -static void xps_map_release(struct rcu_head *rcu) -{ - struct xps_map *map = container_of(rcu, struct xps_map, rcu); - - kfree(map); -} - -static void xps_dev_maps_release(struct rcu_head *rcu) -{ - struct xps_dev_maps *dev_maps = - container_of(rcu, struct xps_dev_maps, rcu); - - kfree(dev_maps); -} - static DEFINE_MUTEX(xps_map_mutex); #define xmap_dereference(P) \ rcu_dereference_protected((P), lockdep_is_held(&xps_map_mutex)) @@ -968,7 +942,7 @@ static ssize_t store_xps_map(struct netdev_queue *queue, } else pos = map_len = alloc_len = 0; - need_set = cpu_isset(cpu, *mask) && cpu_online(cpu); + need_set = cpumask_test_cpu(cpu, mask) && cpu_online(cpu); #ifdef CONFIG_NUMA if (need_set) { if (numa_node == -2) @@ -1009,7 +983,7 @@ static ssize_t store_xps_map(struct netdev_queue *queue, map = dev_maps ? xmap_dereference(dev_maps->cpu_map[cpu]) : NULL; if (map && xmap_dereference(new_dev_maps->cpu_map[cpu]) != map) - call_rcu(&map->rcu, xps_map_release); + kfree_rcu(map, rcu); if (new_dev_maps->cpu_map[cpu]) nonempty = 1; } @@ -1022,7 +996,7 @@ static ssize_t store_xps_map(struct netdev_queue *queue, } if (dev_maps) - call_rcu(&dev_maps->rcu, xps_dev_maps_release); + kfree_rcu(dev_maps, rcu); netdev_queue_numa_node_write(queue, (numa_node >= 0) ? numa_node : NUMA_NO_NODE); @@ -1084,7 +1058,7 @@ static void netdev_queue_release(struct kobject *kobj) else { RCU_INIT_POINTER(dev_maps->cpu_map[i], NULL); - call_rcu(&map->rcu, xps_map_release); + kfree_rcu(map, rcu); map = NULL; } } @@ -1094,7 +1068,7 @@ static void netdev_queue_release(struct kobject *kobj) if (!nonempty) { RCU_INIT_POINTER(dev->xps_maps, NULL); - call_rcu(&dev_maps->rcu, xps_dev_maps_release); + kfree_rcu(dev_maps, rcu); } } diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 1abb5084104..6c6b86d0da1 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -8,6 +8,8 @@ #include <linux/idr.h> #include <linux/rculist.h> #include <linux/nsproxy.h> +#include <linux/proc_fs.h> +#include <linux/file.h> #include <net/net_namespace.h> #include <net/netns/generic.h> @@ -27,14 +29,6 @@ EXPORT_SYMBOL(init_net); #define INITIAL_NET_GEN_PTRS 13 /* +1 for len +2 for rcu_head */ -static void net_generic_release(struct rcu_head *rcu) -{ - struct net_generic *ng; - - ng = container_of(rcu, struct net_generic, rcu); - kfree(ng); -} - static int net_assign_generic(struct net *net, int id, void *data) { struct net_generic *ng, *old_ng; @@ -68,7 +62,7 @@ static int net_assign_generic(struct net *net, int id, void *data) memcpy(&ng->ptr, &old_ng->ptr, old_ng->len * sizeof(void*)); rcu_assign_pointer(net->gen, ng); - call_rcu(&old_ng->rcu, net_generic_release); + kfree_rcu(old_ng, rcu); assign: ng->ptr[id - 1] = data; return 0; @@ -310,6 +304,28 @@ void __put_net(struct net *net) } EXPORT_SYMBOL_GPL(__put_net); +struct net *get_net_ns_by_fd(int fd) +{ + struct proc_inode *ei; + struct file *file; + struct net *net; + + net = ERR_PTR(-EINVAL); + file = proc_ns_fget(fd); + if (!file) + goto out; + + ei = PROC_I(file->f_dentry->d_inode); + if (ei->ns_ops != &netns_operations) + goto out; + + net = get_net(ei->ns); +out: + if (file) + fput(file); + return net; +} + #else struct net *copy_net_ns(unsigned long flags, struct net *old_net) { @@ -317,6 +333,11 @@ struct net *copy_net_ns(unsigned long flags, struct net *old_net) return ERR_PTR(-EINVAL); return old_net; } + +struct net *get_net_ns_by_fd(int fd) +{ + return ERR_PTR(-EINVAL); +} #endif struct net *get_net_ns_by_pid(pid_t pid) @@ -569,3 +590,39 @@ void unregister_pernet_device(struct pernet_operations *ops) mutex_unlock(&net_mutex); } EXPORT_SYMBOL_GPL(unregister_pernet_device); + +#ifdef CONFIG_NET_NS +static void *netns_get(struct task_struct *task) +{ + struct net *net = NULL; + struct nsproxy *nsproxy; + + rcu_read_lock(); + nsproxy = task_nsproxy(task); + if (nsproxy) + net = get_net(nsproxy->net_ns); + rcu_read_unlock(); + + return net; +} + +static void netns_put(void *ns) +{ + put_net(ns); +} + +static int netns_install(struct nsproxy *nsproxy, void *ns) +{ + put_net(nsproxy->net_ns); + nsproxy->net_ns = get_net(ns); + return 0; +} + +const struct proc_ns_operations netns_operations = { + .name = "net", + .type = CLONE_NEWNET, + .get = netns_get, + .put = netns_put, + .install = netns_install, +}; +#endif diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 06be2431753..2d7d6d47378 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -539,7 +539,7 @@ int __netpoll_rx(struct sk_buff *skb) { int proto, len, ulen; int hits = 0; - struct iphdr *iph; + const struct iphdr *iph; struct udphdr *uh; struct netpoll_info *npinfo = skb->dev->npinfo; struct netpoll *np, *tmp; @@ -698,32 +698,8 @@ int netpoll_parse_options(struct netpoll *np, char *opt) if (*cur != 0) { /* MAC address */ - if ((delim = strchr(cur, ':')) == NULL) - goto parse_failed; - *delim = 0; - np->remote_mac[0] = simple_strtol(cur, NULL, 16); - cur = delim + 1; - if ((delim = strchr(cur, ':')) == NULL) - goto parse_failed; - *delim = 0; - np->remote_mac[1] = simple_strtol(cur, NULL, 16); - cur = delim + 1; - if ((delim = strchr(cur, ':')) == NULL) + if (!mac_pton(cur, np->remote_mac)) goto parse_failed; - *delim = 0; - np->remote_mac[2] = simple_strtol(cur, NULL, 16); - cur = delim + 1; - if ((delim = strchr(cur, ':')) == NULL) - goto parse_failed; - *delim = 0; - np->remote_mac[3] = simple_strtol(cur, NULL, 16); - cur = delim + 1; - if ((delim = strchr(cur, ':')) == NULL) - goto parse_failed; - *delim = 0; - np->remote_mac[4] = simple_strtol(cur, NULL, 16); - cur = delim + 1; - np->remote_mac[5] = simple_strtol(cur, NULL, 16); } netpoll_print_options(np); diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 2fa6fee1b46..f76079cd750 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -156,6 +156,7 @@ #include <linux/wait.h> #include <linux/etherdevice.h> #include <linux/kthread.h> +#include <linux/prefetch.h> #include <net/net_namespace.h> #include <net/checksum.h> #include <net/ipv6.h> @@ -449,7 +450,6 @@ static void pktgen_stop(struct pktgen_thread *t); static void pktgen_clear_counters(struct pktgen_dev *pkt_dev); static unsigned int scan_ip6(const char *s, char ip[16]); -static unsigned int fmt_ip6(char *s, const char ip[16]); /* Module parameters, defaults. */ static int pg_count_d __read_mostly = 1000; @@ -556,21 +556,13 @@ static int pktgen_if_show(struct seq_file *seq, void *v) pkt_dev->skb_priority); if (pkt_dev->flags & F_IPV6) { - char b1[128], b2[128], b3[128]; - fmt_ip6(b1, pkt_dev->in6_saddr.s6_addr); - fmt_ip6(b2, pkt_dev->min_in6_saddr.s6_addr); - fmt_ip6(b3, pkt_dev->max_in6_saddr.s6_addr); seq_printf(seq, - " saddr: %s min_saddr: %s max_saddr: %s\n", b1, - b2, b3); - - fmt_ip6(b1, pkt_dev->in6_daddr.s6_addr); - fmt_ip6(b2, pkt_dev->min_in6_daddr.s6_addr); - fmt_ip6(b3, pkt_dev->max_in6_daddr.s6_addr); - seq_printf(seq, - " daddr: %s min_daddr: %s max_daddr: %s\n", b1, - b2, b3); - + " saddr: %pI6c min_saddr: %pI6c max_saddr: %pI6c\n" + " daddr: %pI6c min_daddr: %pI6c max_daddr: %pI6c\n", + &pkt_dev->in6_saddr, + &pkt_dev->min_in6_saddr, &pkt_dev->max_in6_saddr, + &pkt_dev->in6_daddr, + &pkt_dev->min_in6_daddr, &pkt_dev->max_in6_daddr); } else { seq_printf(seq, " dst_min: %s dst_max: %s\n", @@ -706,10 +698,9 @@ static int pktgen_if_show(struct seq_file *seq, void *v) pkt_dev->cur_src_mac_offset); if (pkt_dev->flags & F_IPV6) { - char b1[128], b2[128]; - fmt_ip6(b1, pkt_dev->cur_in6_daddr.s6_addr); - fmt_ip6(b2, pkt_dev->cur_in6_saddr.s6_addr); - seq_printf(seq, " cur_saddr: %s cur_daddr: %s\n", b2, b1); + seq_printf(seq, " cur_saddr: %pI6c cur_daddr: %pI6c\n", + &pkt_dev->cur_in6_saddr, + &pkt_dev->cur_in6_daddr); } else seq_printf(seq, " cur_saddr: 0x%x cur_daddr: 0x%x\n", pkt_dev->cur_saddr, pkt_dev->cur_daddr); @@ -1309,7 +1300,7 @@ static ssize_t pktgen_if_write(struct file *file, buf[len] = 0; scan_ip6(buf, pkt_dev->in6_daddr.s6_addr); - fmt_ip6(buf, pkt_dev->in6_daddr.s6_addr); + snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->in6_daddr); ipv6_addr_copy(&pkt_dev->cur_in6_daddr, &pkt_dev->in6_daddr); @@ -1332,7 +1323,7 @@ static ssize_t pktgen_if_write(struct file *file, buf[len] = 0; scan_ip6(buf, pkt_dev->min_in6_daddr.s6_addr); - fmt_ip6(buf, pkt_dev->min_in6_daddr.s6_addr); + snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->min_in6_daddr); ipv6_addr_copy(&pkt_dev->cur_in6_daddr, &pkt_dev->min_in6_daddr); @@ -1355,7 +1346,7 @@ static ssize_t pktgen_if_write(struct file *file, buf[len] = 0; scan_ip6(buf, pkt_dev->max_in6_daddr.s6_addr); - fmt_ip6(buf, pkt_dev->max_in6_daddr.s6_addr); + snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->max_in6_daddr); if (debug) printk(KERN_DEBUG "pktgen: dst6_max set to: %s\n", buf); @@ -1376,7 +1367,7 @@ static ssize_t pktgen_if_write(struct file *file, buf[len] = 0; scan_ip6(buf, pkt_dev->in6_saddr.s6_addr); - fmt_ip6(buf, pkt_dev->in6_saddr.s6_addr); + snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->in6_saddr); ipv6_addr_copy(&pkt_dev->cur_in6_saddr, &pkt_dev->in6_saddr); @@ -1430,11 +1421,6 @@ static ssize_t pktgen_if_write(struct file *file, return count; } if (!strcmp(name, "dst_mac")) { - char *v = valstr; - unsigned char old_dmac[ETH_ALEN]; - unsigned char *m = pkt_dev->dst_mac; - memcpy(old_dmac, pkt_dev->dst_mac, ETH_ALEN); - len = strn_len(&user_buffer[i], sizeof(valstr) - 1); if (len < 0) return len; @@ -1442,35 +1428,16 @@ static ssize_t pktgen_if_write(struct file *file, memset(valstr, 0, sizeof(valstr)); if (copy_from_user(valstr, &user_buffer[i], len)) return -EFAULT; - i += len; - - for (*m = 0; *v && m < pkt_dev->dst_mac + 6; v++) { - int value; - - value = hex_to_bin(*v); - if (value >= 0) - *m = *m * 16 + value; - - if (*v == ':') { - m++; - *m = 0; - } - } + if (!mac_pton(valstr, pkt_dev->dst_mac)) + return -EINVAL; /* Set up Dest MAC */ - if (compare_ether_addr(old_dmac, pkt_dev->dst_mac)) - memcpy(&(pkt_dev->hh[0]), pkt_dev->dst_mac, ETH_ALEN); + memcpy(&pkt_dev->hh[0], pkt_dev->dst_mac, ETH_ALEN); - sprintf(pg_result, "OK: dstmac"); + sprintf(pg_result, "OK: dstmac %pM", pkt_dev->dst_mac); return count; } if (!strcmp(name, "src_mac")) { - char *v = valstr; - unsigned char old_smac[ETH_ALEN]; - unsigned char *m = pkt_dev->src_mac; - - memcpy(old_smac, pkt_dev->src_mac, ETH_ALEN); - len = strn_len(&user_buffer[i], sizeof(valstr) - 1); if (len < 0) return len; @@ -1478,26 +1445,13 @@ static ssize_t pktgen_if_write(struct file *file, memset(valstr, 0, sizeof(valstr)); if (copy_from_user(valstr, &user_buffer[i], len)) return -EFAULT; - i += len; - - for (*m = 0; *v && m < pkt_dev->src_mac + 6; v++) { - int value; - - value = hex_to_bin(*v); - if (value >= 0) - *m = *m * 16 + value; - - if (*v == ':') { - m++; - *m = 0; - } - } + if (!mac_pton(valstr, pkt_dev->src_mac)) + return -EINVAL; /* Set up Src MAC */ - if (compare_ether_addr(old_smac, pkt_dev->src_mac)) - memcpy(&(pkt_dev->hh[6]), pkt_dev->src_mac, ETH_ALEN); + memcpy(&pkt_dev->hh[6], pkt_dev->src_mac, ETH_ALEN); - sprintf(pg_result, "OK: srcmac"); + sprintf(pg_result, "OK: srcmac %pM", pkt_dev->src_mac); return count; } @@ -2622,6 +2576,7 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb, } else { int frags = pkt_dev->nfrags; int i, len; + int frag_len; if (frags > MAX_SKB_FRAGS) @@ -2633,6 +2588,8 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb, } i = 0; + frag_len = (datalen/frags) < PAGE_SIZE ? + (datalen/frags) : PAGE_SIZE; while (datalen > 0) { if (unlikely(!pkt_dev->page)) { int node = numa_node_id(); @@ -2646,38 +2603,18 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb, skb_shinfo(skb)->frags[i].page = pkt_dev->page; get_page(pkt_dev->page); skb_shinfo(skb)->frags[i].page_offset = 0; - skb_shinfo(skb)->frags[i].size = - (datalen < PAGE_SIZE ? datalen : PAGE_SIZE); + /*last fragment, fill rest of data*/ + if (i == (frags - 1)) + skb_shinfo(skb)->frags[i].size = + (datalen < PAGE_SIZE ? datalen : PAGE_SIZE); + else + skb_shinfo(skb)->frags[i].size = frag_len; datalen -= skb_shinfo(skb)->frags[i].size; skb->len += skb_shinfo(skb)->frags[i].size; skb->data_len += skb_shinfo(skb)->frags[i].size; i++; skb_shinfo(skb)->nr_frags = i; } - - while (i < frags) { - int rem; - - if (i == 0) - break; - - rem = skb_shinfo(skb)->frags[i - 1].size / 2; - if (rem == 0) - break; - - skb_shinfo(skb)->frags[i - 1].size -= rem; - - skb_shinfo(skb)->frags[i] = - skb_shinfo(skb)->frags[i - 1]; - get_page(skb_shinfo(skb)->frags[i].page); - skb_shinfo(skb)->frags[i].page = - skb_shinfo(skb)->frags[i - 1].page; - skb_shinfo(skb)->frags[i].page_offset += - skb_shinfo(skb)->frags[i - 1].size; - skb_shinfo(skb)->frags[i].size = rem; - i++; - skb_shinfo(skb)->nr_frags = i; - } } /* Stamp the time, and sequence number, @@ -2915,79 +2852,6 @@ static unsigned int scan_ip6(const char *s, char ip[16]) return len; } -static char tohex(char hexdigit) -{ - return hexdigit > 9 ? hexdigit + 'a' - 10 : hexdigit + '0'; -} - -static int fmt_xlong(char *s, unsigned int i) -{ - char *bak = s; - *s = tohex((i >> 12) & 0xf); - if (s != bak || *s != '0') - ++s; - *s = tohex((i >> 8) & 0xf); - if (s != bak || *s != '0') - ++s; - *s = tohex((i >> 4) & 0xf); - if (s != bak || *s != '0') - ++s; - *s = tohex(i & 0xf); - return s - bak + 1; -} - -static unsigned int fmt_ip6(char *s, const char ip[16]) -{ - unsigned int len; - unsigned int i; - unsigned int temp; - unsigned int compressing; - int j; - - len = 0; - compressing = 0; - for (j = 0; j < 16; j += 2) { - -#ifdef V4MAPPEDPREFIX - if (j == 12 && !memcmp(ip, V4mappedprefix, 12)) { - inet_ntoa_r(*(struct in_addr *)(ip + 12), s); - temp = strlen(s); - return len + temp; - } -#endif - temp = ((unsigned long)(unsigned char)ip[j] << 8) + - (unsigned long)(unsigned char)ip[j + 1]; - if (temp == 0) { - if (!compressing) { - compressing = 1; - if (j == 0) { - *s++ = ':'; - ++len; - } - } - } else { - if (compressing) { - compressing = 0; - *s++ = ':'; - ++len; - } - i = fmt_xlong(s, temp); - len += i; - s += i; - if (j < 14) { - *s++ = ':'; - ++len; - } - } - } - if (compressing) { - *s++ = ':'; - ++len; - } - *s = 0; - return len; -} - static struct sk_buff *fill_packet_ipv6(struct net_device *odev, struct pktgen_dev *pkt_dev) { @@ -3680,13 +3544,12 @@ static int pktgen_add_device(struct pktgen_thread *t, const char *ifname) return -ENOMEM; strcpy(pkt_dev->odevname, ifname); - pkt_dev->flows = vmalloc_node(MAX_CFLOWS * sizeof(struct flow_state), + pkt_dev->flows = vzalloc_node(MAX_CFLOWS * sizeof(struct flow_state), node); if (pkt_dev->flows == NULL) { kfree(pkt_dev); return -ENOMEM; } - memset(pkt_dev->flows, 0, MAX_CFLOWS * sizeof(struct flow_state)); pkt_dev->removal_mark = 0; pkt_dev->min_pkt_size = ETH_ZLEN; @@ -3844,6 +3707,7 @@ static int __init pg_init(void) { int cpu; struct proc_dir_entry *pe; + int ret = 0; pr_info("%s", version); @@ -3854,11 +3718,10 @@ static int __init pg_init(void) pe = proc_create(PGCTRL, 0600, pg_proc_dir, &pktgen_fops); if (pe == NULL) { pr_err("ERROR: cannot create %s procfs entry\n", PGCTRL); - proc_net_remove(&init_net, PG_PROC_DIR); - return -EINVAL; + ret = -EINVAL; + goto remove_dir; } - /* Register us to receive netdevice events */ register_netdevice_notifier(&pktgen_notifier_block); for_each_online_cpu(cpu) { @@ -3872,13 +3735,18 @@ static int __init pg_init(void) if (list_empty(&pktgen_threads)) { pr_err("ERROR: Initialization failed for all threads\n"); - unregister_netdevice_notifier(&pktgen_notifier_block); - remove_proc_entry(PGCTRL, pg_proc_dir); - proc_net_remove(&init_net, PG_PROC_DIR); - return -ENODEV; + ret = -ENODEV; + goto unregister; } return 0; + + unregister: + unregister_netdevice_notifier(&pktgen_notifier_block); + remove_proc_entry(PGCTRL, pg_proc_dir); + remove_dir: + proc_net_remove(&init_net, PG_PROC_DIR); + return ret; } static void __exit pg_cleanup(void) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index d7c4bb4b182..a798fc6f2aa 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -56,9 +56,11 @@ struct rtnl_link { rtnl_doit_func doit; rtnl_dumpit_func dumpit; + rtnl_calcit_func calcit; }; static DEFINE_MUTEX(rtnl_mutex); +static u16 min_ifinfo_dump_size; void rtnl_lock(void) { @@ -144,12 +146,28 @@ static rtnl_dumpit_func rtnl_get_dumpit(int protocol, int msgindex) return tab ? tab[msgindex].dumpit : NULL; } +static rtnl_calcit_func rtnl_get_calcit(int protocol, int msgindex) +{ + struct rtnl_link *tab; + + if (protocol <= RTNL_FAMILY_MAX) + tab = rtnl_msg_handlers[protocol]; + else + tab = NULL; + + if (tab == NULL || tab[msgindex].calcit == NULL) + tab = rtnl_msg_handlers[PF_UNSPEC]; + + return tab ? tab[msgindex].calcit : NULL; +} + /** * __rtnl_register - Register a rtnetlink message type * @protocol: Protocol family or PF_UNSPEC * @msgtype: rtnetlink message type * @doit: Function pointer called for each request message * @dumpit: Function pointer called for each dump request (NLM_F_DUMP) message + * @calcit: Function pointer to calc size of dump message * * Registers the specified function pointers (at least one of them has * to be non-NULL) to be called whenever a request message for the @@ -162,7 +180,8 @@ static rtnl_dumpit_func rtnl_get_dumpit(int protocol, int msgindex) * Returns 0 on success or a negative error code. */ int __rtnl_register(int protocol, int msgtype, - rtnl_doit_func doit, rtnl_dumpit_func dumpit) + rtnl_doit_func doit, rtnl_dumpit_func dumpit, + rtnl_calcit_func calcit) { struct rtnl_link *tab; int msgindex; @@ -185,6 +204,9 @@ int __rtnl_register(int protocol, int msgtype, if (dumpit) tab[msgindex].dumpit = dumpit; + if (calcit) + tab[msgindex].calcit = calcit; + return 0; } EXPORT_SYMBOL_GPL(__rtnl_register); @@ -199,9 +221,10 @@ EXPORT_SYMBOL_GPL(__rtnl_register); * of memory implies no sense in continuing. */ void rtnl_register(int protocol, int msgtype, - rtnl_doit_func doit, rtnl_dumpit_func dumpit) + rtnl_doit_func doit, rtnl_dumpit_func dumpit, + rtnl_calcit_func calcit) { - if (__rtnl_register(protocol, msgtype, doit, dumpit) < 0) + if (__rtnl_register(protocol, msgtype, doit, dumpit, calcit) < 0) panic("Unable to register rtnetlink message handler, " "protocol = %d, message type = %d\n", protocol, msgtype); @@ -850,6 +873,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, struct nlattr *attr, *af_spec; struct rtnl_af_ops *af_ops; + ASSERT_RTNL(); nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ifm), flags); if (nlh == NULL) return -EMSGSIZE; @@ -1007,10 +1031,11 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) s_h = cb->args[0]; s_idx = cb->args[1]; + rcu_read_lock(); for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { idx = 0; head = &net->dev_index_head[h]; - hlist_for_each_entry(dev, node, head, index_hlist) { + hlist_for_each_entry_rcu(dev, node, head, index_hlist) { if (idx < s_idx) goto cont; if (rtnl_fill_ifinfo(skb, dev, RTM_NEWLINK, @@ -1023,6 +1048,7 @@ cont: } } out: + rcu_read_unlock(); cb->args[1] = idx; cb->args[0] = h; @@ -1043,6 +1069,7 @@ const struct nla_policy ifla_policy[IFLA_MAX+1] = { [IFLA_LINKMODE] = { .type = NLA_U8 }, [IFLA_LINKINFO] = { .type = NLA_NESTED }, [IFLA_NET_NS_PID] = { .type = NLA_U32 }, + [IFLA_NET_NS_FD] = { .type = NLA_U32 }, [IFLA_IFALIAS] = { .type = NLA_STRING, .len = IFALIASZ-1 }, [IFLA_VFINFO_LIST] = {. type = NLA_NESTED }, [IFLA_VF_PORTS] = { .type = NLA_NESTED }, @@ -1091,6 +1118,8 @@ struct net *rtnl_link_get_net(struct net *src_net, struct nlattr *tb[]) */ if (tb[IFLA_NET_NS_PID]) net = get_net_ns_by_pid(nla_get_u32(tb[IFLA_NET_NS_PID])); + else if (tb[IFLA_NET_NS_FD]) + net = get_net_ns_by_fd(nla_get_u32(tb[IFLA_NET_NS_FD])); else net = get_net(src_net); return net; @@ -1221,7 +1250,7 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm, int send_addr_notify = 0; int err; - if (tb[IFLA_NET_NS_PID]) { + if (tb[IFLA_NET_NS_PID] || tb[IFLA_NET_NS_FD]) { struct net *net = rtnl_link_get_net(dev_net(dev), tb); if (IS_ERR(net)) { err = PTR_ERR(net); @@ -1499,6 +1528,7 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) char ifname[IFNAMSIZ]; struct nlattr *tb[IFLA_MAX+1]; int err; + LIST_HEAD(list_kill); err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy); if (err < 0) @@ -1522,7 +1552,9 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) if (!ops) return -EOPNOTSUPP; - ops->dellink(dev, NULL); + ops->dellink(dev, &list_kill); + unregister_netdevice_many(&list_kill); + list_del(&list_kill); return 0; } @@ -1570,12 +1602,6 @@ struct net_device *rtnl_create_link(struct net *src_net, struct net *net, dev->rtnl_link_state = RTNL_LINK_INITIALIZING; dev->real_num_tx_queues = real_num_queues; - if (strchr(dev->name, '%')) { - err = dev_alloc_name(dev, dev->name); - if (err < 0) - goto err_free; - } - if (tb[IFLA_MTU]) dev->mtu = nla_get_u32(tb[IFLA_MTU]); if (tb[IFLA_ADDRESS]) @@ -1595,8 +1621,6 @@ struct net_device *rtnl_create_link(struct net *src_net, struct net *net, return dev; -err_free: - free_netdev(dev); err: return ERR_PTR(err); } @@ -1817,6 +1841,11 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) return err; } +static u16 rtnl_calcit(struct sk_buff *skb) +{ + return min_ifinfo_dump_size; +} + static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb) { int idx; @@ -1846,11 +1875,14 @@ void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change) struct net *net = dev_net(dev); struct sk_buff *skb; int err = -ENOBUFS; + size_t if_info_size; - skb = nlmsg_new(if_nlmsg_size(dev), GFP_KERNEL); + skb = nlmsg_new((if_info_size = if_nlmsg_size(dev)), GFP_KERNEL); if (skb == NULL) goto errout; + min_ifinfo_dump_size = max_t(u16, if_info_size, min_ifinfo_dump_size); + err = rtnl_fill_ifinfo(skb, dev, type, 0, 0, change, 0); if (err < 0) { /* -EMSGSIZE implies BUG in if_nlmsg_size() */ @@ -1901,14 +1933,20 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) { struct sock *rtnl; rtnl_dumpit_func dumpit; + rtnl_calcit_func calcit; + u16 min_dump_alloc = 0; dumpit = rtnl_get_dumpit(family, type); if (dumpit == NULL) return -EOPNOTSUPP; + calcit = rtnl_get_calcit(family, type); + if (calcit) + min_dump_alloc = calcit(skb); __rtnl_unlock(); rtnl = net->rtnl; - err = netlink_dump_start(rtnl, skb, nlh, dumpit, NULL); + err = netlink_dump_start(rtnl, skb, nlh, dumpit, + NULL, min_dump_alloc); rtnl_lock(); return err; } @@ -1963,6 +2001,8 @@ static int rtnetlink_event(struct notifier_block *this, unsigned long event, voi case NETDEV_GOING_DOWN: case NETDEV_UNREGISTER: case NETDEV_UNREGISTER_BATCH: + case NETDEV_RELEASE: + case NETDEV_JOIN: break; default: rtmsg_ifinfo(RTM_NEWLINK, dev, 0); @@ -2016,12 +2056,13 @@ void __init rtnetlink_init(void) netlink_set_nonroot(NETLINK_ROUTE, NL_NONROOT_RECV); register_netdevice_notifier(&rtnetlink_dev_notifier); - rtnl_register(PF_UNSPEC, RTM_GETLINK, rtnl_getlink, rtnl_dump_ifinfo); - rtnl_register(PF_UNSPEC, RTM_SETLINK, rtnl_setlink, NULL); - rtnl_register(PF_UNSPEC, RTM_NEWLINK, rtnl_newlink, NULL); - rtnl_register(PF_UNSPEC, RTM_DELLINK, rtnl_dellink, NULL); + rtnl_register(PF_UNSPEC, RTM_GETLINK, rtnl_getlink, + rtnl_dump_ifinfo, rtnl_calcit); + rtnl_register(PF_UNSPEC, RTM_SETLINK, rtnl_setlink, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_NEWLINK, rtnl_newlink, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_DELLINK, rtnl_dellink, NULL, NULL); - rtnl_register(PF_UNSPEC, RTM_GETADDR, NULL, rtnl_dump_all); - rtnl_register(PF_UNSPEC, RTM_GETROUTE, NULL, rtnl_dump_all); + rtnl_register(PF_UNSPEC, RTM_GETADDR, NULL, rtnl_dump_all, NULL); + rtnl_register(PF_UNSPEC, RTM_GETROUTE, NULL, rtnl_dump_all, NULL); } diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 7ebeed0a877..46cbd28f40f 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -57,6 +57,7 @@ #include <linux/init.h> #include <linux/scatterlist.h> #include <linux/errqueue.h> +#include <linux/prefetch.h> #include <net/protocol.h> #include <net/dst.h> @@ -2993,6 +2994,9 @@ int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb) skb->destructor = sock_rmem_free; atomic_add(skb->truesize, &sk->sk_rmem_alloc); + /* before exiting rcu section, make sure dst is refcounted */ + skb_dst_force(skb); + skb_queue_tail(&sk->sk_error_queue, skb); if (!sock_flag(sk, SOCK_DEAD)) sk->sk_data_ready(sk, skb->len); diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index 385b6095fdc..77a65f03148 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -17,6 +17,7 @@ #include <net/ip.h> #include <net/sock.h> +#include <net/net_ratelimit.h> #ifdef CONFIG_RPS static int rps_sock_flow_sysctl(ctl_table *table, int write, @@ -122,6 +123,15 @@ static struct ctl_table net_core_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, +#ifdef CONFIG_BPF_JIT + { + .procname = "bpf_jit_enable", + .data = &bpf_jit_enable, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, +#endif { .procname = "netdev_tstamp_prequeue", .data = &netdev_tstamp_prequeue, diff --git a/net/core/timestamping.c b/net/core/timestamping.c index 7e7ca375d43..3b00a6b0273 100644 --- a/net/core/timestamping.c +++ b/net/core/timestamping.c @@ -68,6 +68,7 @@ void skb_clone_tx_timestamp(struct sk_buff *skb) break; } } +EXPORT_SYMBOL_GPL(skb_clone_tx_timestamp); void skb_complete_tx_timestamp(struct sk_buff *skb, struct skb_shared_hwtstamps *hwtstamps) diff --git a/net/core/utils.c b/net/core/utils.c index 5fea0ab2190..386e263f606 100644 --- a/net/core/utils.c +++ b/net/core/utils.c @@ -27,6 +27,7 @@ #include <linux/ratelimit.h> #include <net/sock.h> +#include <net/net_ratelimit.h> #include <asm/byteorder.h> #include <asm/system.h> @@ -296,3 +297,27 @@ void inet_proto_csum_replace4(__sum16 *sum, struct sk_buff *skb, csum_unfold(*sum))); } EXPORT_SYMBOL(inet_proto_csum_replace4); + +int mac_pton(const char *s, u8 *mac) +{ + int i; + + /* XX:XX:XX:XX:XX:XX */ + if (strlen(s) < 3 * ETH_ALEN - 1) + return 0; + + /* Don't dirty result unless string is valid MAC. */ + for (i = 0; i < ETH_ALEN; i++) { + if (!strchr("0123456789abcdefABCDEF", s[i * 3])) + return 0; + if (!strchr("0123456789abcdefABCDEF", s[i * 3 + 1])) + return 0; + if (i != ETH_ALEN - 1 && s[i * 3 + 2] != ':') + return 0; + } + for (i = 0; i < ETH_ALEN; i++) { + mac[i] = (hex_to_bin(s[i * 3]) << 4) | hex_to_bin(s[i * 3 + 1]); + } + return 1; +} +EXPORT_SYMBOL(mac_pton); diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c index 3609eacaf4c..ed1bb8c65a9 100644 --- a/net/dcb/dcbnl.c +++ b/net/dcb/dcbnl.c @@ -1819,8 +1819,8 @@ static int __init dcbnl_init(void) { INIT_LIST_HEAD(&dcb_app_list); - rtnl_register(PF_UNSPEC, RTM_GETDCB, dcb_doit, NULL); - rtnl_register(PF_UNSPEC, RTM_SETDCB, dcb_doit, NULL); + rtnl_register(PF_UNSPEC, RTM_GETDCB, dcb_doit, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_SETDCB, dcb_doit, NULL, NULL); return 0; } diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index ae451c6d83b..8c36adfd191 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -40,13 +40,15 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) { + const struct sockaddr_in *usin = (struct sockaddr_in *)uaddr; struct inet_sock *inet = inet_sk(sk); struct dccp_sock *dp = dccp_sk(sk); - const struct sockaddr_in *usin = (struct sockaddr_in *)uaddr; __be16 orig_sport, orig_dport; - struct rtable *rt; __be32 daddr, nexthop; + struct flowi4 *fl4; + struct rtable *rt; int err; + struct ip_options_rcu *inet_opt; dp->dccps_role = DCCP_ROLE_CLIENT; @@ -57,15 +59,19 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) return -EAFNOSUPPORT; nexthop = daddr = usin->sin_addr.s_addr; - if (inet->opt != NULL && inet->opt->srr) { + + inet_opt = rcu_dereference_protected(inet->inet_opt, + sock_owned_by_user(sk)); + if (inet_opt != NULL && inet_opt->opt.srr) { if (daddr == 0) return -EINVAL; - nexthop = inet->opt->faddr; + nexthop = inet_opt->opt.faddr; } orig_sport = inet->inet_sport; orig_dport = usin->sin_port; - rt = ip_route_connect(nexthop, inet->inet_saddr, + fl4 = &inet->cork.fl.u.ip4; + rt = ip_route_connect(fl4, nexthop, inet->inet_saddr, RT_CONN_FLAGS(sk), sk->sk_bound_dev_if, IPPROTO_DCCP, orig_sport, orig_dport, sk, true); @@ -77,19 +83,19 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) return -ENETUNREACH; } - if (inet->opt == NULL || !inet->opt->srr) - daddr = rt->rt_dst; + if (inet_opt == NULL || !inet_opt->opt.srr) + daddr = fl4->daddr; if (inet->inet_saddr == 0) - inet->inet_saddr = rt->rt_src; + inet->inet_saddr = fl4->saddr; inet->inet_rcv_saddr = inet->inet_saddr; inet->inet_dport = usin->sin_port; inet->inet_daddr = daddr; inet_csk(sk)->icsk_ext_hdr_len = 0; - if (inet->opt != NULL) - inet_csk(sk)->icsk_ext_hdr_len = inet->opt->optlen; + if (inet_opt) + inet_csk(sk)->icsk_ext_hdr_len = inet_opt->opt.optlen; /* * Socket identity is still unknown (sport may be zero). * However we set state to DCCP_REQUESTING and not releasing socket @@ -101,8 +107,7 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) if (err != 0) goto failure; - rt = ip_route_newports(rt, IPPROTO_DCCP, - orig_sport, orig_dport, + rt = ip_route_newports(fl4, rt, orig_sport, orig_dport, inet->inet_sport, inet->inet_dport, sk); if (IS_ERR(rt)) { rt = NULL; @@ -391,32 +396,30 @@ struct sock *dccp_v4_request_recv_sock(struct sock *sk, struct sk_buff *skb, if (sk_acceptq_is_full(sk)) goto exit_overflow; - if (dst == NULL && (dst = inet_csk_route_req(sk, req)) == NULL) - goto exit; - newsk = dccp_create_openreq_child(sk, req, skb); if (newsk == NULL) goto exit_nonewsk; - sk_setup_caps(newsk, dst); - newinet = inet_sk(newsk); ireq = inet_rsk(req); newinet->inet_daddr = ireq->rmt_addr; newinet->inet_rcv_saddr = ireq->loc_addr; newinet->inet_saddr = ireq->loc_addr; - newinet->opt = ireq->opt; + newinet->inet_opt = ireq->opt; ireq->opt = NULL; newinet->mc_index = inet_iif(skb); newinet->mc_ttl = ip_hdr(skb)->ttl; newinet->inet_id = jiffies; + if (dst == NULL && (dst = inet_csk_route_child_sock(sk, newsk, req)) == NULL) + goto put_and_exit; + + sk_setup_caps(newsk, dst); + dccp_sync_mss(newsk, dst_mtu(dst)); - if (__inet_inherit_port(sk, newsk) < 0) { - sock_put(newsk); - goto exit; - } + if (__inet_inherit_port(sk, newsk) < 0) + goto put_and_exit; __inet_hash_nolisten(newsk, NULL); return newsk; @@ -428,6 +431,9 @@ exit_nonewsk: exit: NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); return NULL; +put_and_exit: + sock_put(newsk); + goto exit; } EXPORT_SYMBOL_GPL(dccp_v4_request_recv_sock); @@ -491,8 +497,9 @@ static int dccp_v4_send_response(struct sock *sk, struct request_sock *req, int err = -1; struct sk_buff *skb; struct dst_entry *dst; + struct flowi4 fl4; - dst = inet_csk_route_req(sk, req); + dst = inet_csk_route_req(sk, &fl4, req); if (dst == NULL) goto out; diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index de1b7e37ad5..8dc4348774a 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -54,8 +54,8 @@ static void dccp_v6_hash(struct sock *sk) /* add pseudo-header to DCCP checksum stored in skb->csum */ static inline __sum16 dccp_v6_csum_finish(struct sk_buff *skb, - struct in6_addr *saddr, - struct in6_addr *daddr) + const struct in6_addr *saddr, + const struct in6_addr *daddr) { return csum_ipv6_magic(saddr, daddr, skb->len, IPPROTO_DCCP, skb->csum); } @@ -87,7 +87,7 @@ static inline __u32 dccp_v6_init_sequence(struct sk_buff *skb) static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, u8 type, u8 code, int offset, __be32 info) { - struct ipv6hdr *hdr = (struct ipv6hdr *)skb->data; + const struct ipv6hdr *hdr = (const struct ipv6hdr *)skb->data; const struct dccp_hdr *dh = (struct dccp_hdr *)(skb->data + offset); struct dccp_sock *dp; struct ipv6_pinfo *np; @@ -296,7 +296,7 @@ static void dccp_v6_reqsk_destructor(struct request_sock *req) static void dccp_v6_ctl_send_reset(struct sock *sk, struct sk_buff *rxskb) { - struct ipv6hdr *rxip6h; + const struct ipv6hdr *rxip6h; struct sk_buff *skb; struct flowi6 fl6; struct net *net = dev_net(skb_dst(rxskb)->dev); @@ -573,7 +573,7 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, First: no IPv4 options. */ - newinet->opt = NULL; + newinet->inet_opt = NULL; /* Clone RX bits */ newnp->rxopt.all = np->rxopt.all; diff --git a/net/dccp/options.c b/net/dccp/options.c index f06ffcfc8d7..4b2ab657ac8 100644 --- a/net/dccp/options.c +++ b/net/dccp/options.c @@ -123,6 +123,8 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq, case DCCPO_CHANGE_L ... DCCPO_CONFIRM_R: if (pkt_type == DCCP_PKT_DATA) /* RFC 4340, 6 */ break; + if (len == 0) + goto out_invalid_option; rc = dccp_feat_parse_options(sk, dreq, mandatory, opt, *value, value + 1, len - 1); if (rc) diff --git a/net/dccp/output.c b/net/dccp/output.c index 136d41cbcd0..fab108e51e5 100644 --- a/net/dccp/output.c +++ b/net/dccp/output.c @@ -43,7 +43,7 @@ static void dccp_skb_entail(struct sock *sk, struct sk_buff *skb) static int dccp_transmit_skb(struct sock *sk, struct sk_buff *skb) { if (likely(skb != NULL)) { - const struct inet_sock *inet = inet_sk(sk); + struct inet_sock *inet = inet_sk(sk); const struct inet_connection_sock *icsk = inet_csk(sk); struct dccp_sock *dp = dccp_sk(sk); struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb); @@ -136,7 +136,7 @@ static int dccp_transmit_skb(struct sock *sk, struct sk_buff *skb) DCCP_INC_STATS(DCCP_MIB_OUTSEGS); - err = icsk->icsk_af_ops->queue_xmit(skb); + err = icsk->icsk_af_ops->queue_xmit(skb, &inet->cork.fl); return net_xmit_eval(err); } return -ENOBUFS; diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c index 0dcaa903e00..3780fd6e7bf 100644 --- a/net/decnet/dn_dev.c +++ b/net/decnet/dn_dev.c @@ -332,14 +332,9 @@ static struct dn_ifaddr *dn_dev_alloc_ifa(void) return ifa; } -static void dn_dev_free_ifa_rcu(struct rcu_head *head) -{ - kfree(container_of(head, struct dn_ifaddr, rcu)); -} - static void dn_dev_free_ifa(struct dn_ifaddr *ifa) { - call_rcu(&ifa->rcu, dn_dev_free_ifa_rcu); + kfree_rcu(ifa, rcu); } static void dn_dev_del_ifa(struct dn_dev *dn_db, struct dn_ifaddr __rcu **ifap, int destroy) @@ -752,7 +747,8 @@ static int dn_nl_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) skip_naddr = cb->args[1]; idx = 0; - for_each_netdev(&init_net, dev) { + rcu_read_lock(); + for_each_netdev_rcu(&init_net, dev) { if (idx < skip_ndevs) goto cont; else if (idx > skip_ndevs) { @@ -761,11 +757,11 @@ static int dn_nl_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) skip_naddr = 0; } - if ((dn_db = rtnl_dereference(dev->dn_ptr)) == NULL) + if ((dn_db = rcu_dereference(dev->dn_ptr)) == NULL) goto cont; - for (ifa = rtnl_dereference(dn_db->ifa_list), dn_idx = 0; ifa; - ifa = rtnl_dereference(ifa->ifa_next), dn_idx++) { + for (ifa = rcu_dereference(dn_db->ifa_list), dn_idx = 0; ifa; + ifa = rcu_dereference(ifa->ifa_next), dn_idx++) { if (dn_idx < skip_naddr) continue; @@ -778,6 +774,7 @@ cont: idx++; } done: + rcu_read_unlock(); cb->args[0] = idx; cb->args[1] = dn_idx; @@ -1417,9 +1414,9 @@ void __init dn_dev_init(void) dn_dev_devices_on(); - rtnl_register(PF_DECnet, RTM_NEWADDR, dn_nl_newaddr, NULL); - rtnl_register(PF_DECnet, RTM_DELADDR, dn_nl_deladdr, NULL); - rtnl_register(PF_DECnet, RTM_GETADDR, NULL, dn_nl_dump_ifaddr); + rtnl_register(PF_DECnet, RTM_NEWADDR, dn_nl_newaddr, NULL, NULL); + rtnl_register(PF_DECnet, RTM_DELADDR, dn_nl_deladdr, NULL, NULL); + rtnl_register(PF_DECnet, RTM_GETADDR, NULL, dn_nl_dump_ifaddr, NULL); proc_net_fops_create(&init_net, "decnet_dev", S_IRUGO, &dn_dev_seq_fops); diff --git a/net/decnet/dn_fib.c b/net/decnet/dn_fib.c index 1c74ed36ce8..104324d6d53 100644 --- a/net/decnet/dn_fib.c +++ b/net/decnet/dn_fib.c @@ -763,8 +763,8 @@ void __init dn_fib_init(void) register_dnaddr_notifier(&dn_fib_dnaddr_notifier); - rtnl_register(PF_DECnet, RTM_NEWROUTE, dn_fib_rtm_newroute, NULL); - rtnl_register(PF_DECnet, RTM_DELROUTE, dn_fib_rtm_delroute, NULL); + rtnl_register(PF_DECnet, RTM_NEWROUTE, dn_fib_rtm_newroute, NULL, NULL); + rtnl_register(PF_DECnet, RTM_DELROUTE, dn_fib_rtm_delroute, NULL, NULL); } diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 9f09d4fc288..2949ca474ed 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -1125,13 +1125,11 @@ make_route: if (dev_out->flags & IFF_LOOPBACK) flags |= RTCF_LOCAL; - rt = dst_alloc(&dn_dst_ops, 0); + rt = dst_alloc(&dn_dst_ops, dev_out, 1, 0, DST_HOST); if (rt == NULL) goto e_nobufs; - atomic_set(&rt->dst.__refcnt, 1); - rt->dst.flags = DST_HOST; - + memset(&rt->fld, 0, sizeof(rt->fld)); rt->fld.saddr = oldflp->saddr; rt->fld.daddr = oldflp->daddr; rt->fld.flowidn_oif = oldflp->flowidn_oif; @@ -1146,8 +1144,6 @@ make_route: rt->rt_dst_map = fld.daddr; rt->rt_src_map = fld.saddr; - rt->dst.dev = dev_out; - dev_hold(dev_out); rt->dst.neighbour = neigh; neigh = NULL; @@ -1399,10 +1395,11 @@ static int dn_route_input_slow(struct sk_buff *skb) } make_route: - rt = dst_alloc(&dn_dst_ops, 0); + rt = dst_alloc(&dn_dst_ops, out_dev, 0, 0, DST_HOST); if (rt == NULL) goto e_nobufs; + memset(&rt->fld, 0, sizeof(rt->fld)); rt->rt_saddr = fld.saddr; rt->rt_daddr = fld.daddr; rt->rt_gateway = fld.daddr; @@ -1419,9 +1416,7 @@ make_route: rt->fld.flowidn_iif = in_dev->ifindex; rt->fld.flowidn_mark = fld.flowidn_mark; - rt->dst.flags = DST_HOST; rt->dst.neighbour = neigh; - rt->dst.dev = out_dev; rt->dst.lastuse = jiffies; rt->dst.output = dn_rt_bug; switch(res.type) { @@ -1440,8 +1435,6 @@ make_route: rt->dst.input = dst_discard; } rt->rt_flags = flags; - if (rt->dst.dev) - dev_hold(rt->dst.dev); err = dn_rt_set_next_hop(rt, &res); if (err) @@ -1848,10 +1841,11 @@ void __init dn_route_init(void) proc_net_fops_create(&init_net, "decnet_cache", S_IRUGO, &dn_rt_cache_seq_fops); #ifdef CONFIG_DECNET_ROUTER - rtnl_register(PF_DECnet, RTM_GETROUTE, dn_cache_getroute, dn_fib_dump); + rtnl_register(PF_DECnet, RTM_GETROUTE, dn_cache_getroute, + dn_fib_dump, NULL); #else rtnl_register(PF_DECnet, RTM_GETROUTE, dn_cache_getroute, - dn_cache_dump); + dn_cache_dump, NULL); #endif } diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c index cfa7a5e1c5c..fa000d26dc6 100644 --- a/net/dns_resolver/dns_key.c +++ b/net/dns_resolver/dns_key.c @@ -212,10 +212,12 @@ static void dns_resolver_describe(const struct key *key, struct seq_file *m) int err = key->type_data.x[0]; seq_puts(m, key->description); - if (err) - seq_printf(m, ": %d", err); - else - seq_printf(m, ": %u", key->datalen); + if (key_is_instantiated(key)) { + if (err) + seq_printf(m, ": %d", err); + else + seq_printf(m, ": %u", key->datalen); + } } /* diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index 87bb5f4de0e..c53ded2a98d 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig @@ -41,12 +41,12 @@ config NET_DSA_MV88E6XXX_NEED_PPU default n config NET_DSA_MV88E6131 - bool "Marvell 88E6095/6095F/6131 ethernet switch chip support" + bool "Marvell 88E6085/6095/6095F/6131 ethernet switch chip support" select NET_DSA_MV88E6XXX select NET_DSA_MV88E6XXX_NEED_PPU select NET_DSA_TAG_DSA ---help--- - This enables support for the Marvell 88E6095/6095F/6131 + This enables support for the Marvell 88E6085/6095/6095F/6131 ethernet switch chips. config NET_DSA_MV88E6123_61_65 diff --git a/net/dsa/mv88e6131.c b/net/dsa/mv88e6131.c index 3da418894ef..45f7411e90b 100644 --- a/net/dsa/mv88e6131.c +++ b/net/dsa/mv88e6131.c @@ -207,8 +207,15 @@ static int mv88e6131_setup_port(struct dsa_switch *ds, int p) * mode, but do not enable forwarding of unknown unicasts. */ val = 0x0433; - if (p == dsa_upstream_port(ds)) + if (p == dsa_upstream_port(ds)) { val |= 0x0104; + /* + * On 6085, unknown multicast forward is controlled + * here rather than in Port Control 2 register. + */ + if (ps->id == ID_6085) + val |= 0x0008; + } if (ds->dsa_port_mask & (1 << p)) val |= 0x0100; REG_WRITE(addr, 0x04, val); @@ -251,10 +258,19 @@ static int mv88e6131_setup_port(struct dsa_switch *ds, int p) * If this is the upstream port for this switch, enable * forwarding of unknown multicast addresses. */ - val = 0x0080 | dsa_upstream_port(ds); - if (p == dsa_upstream_port(ds)) - val |= 0x0040; - REG_WRITE(addr, 0x08, val); + if (ps->id == ID_6085) + /* + * on 6085, bits 3:0 are reserved, bit 6 control ARP + * mirroring, and multicast forward is handled in + * Port Control register. + */ + REG_WRITE(addr, 0x08, 0x0080); + else { + val = 0x0080 | dsa_upstream_port(ds); + if (p == dsa_upstream_port(ds)) + val |= 0x0040; + REG_WRITE(addr, 0x08, val); + } /* * Rate Control: disable ingress rate limiting. diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 64ca2a6fa0d..0a47b6c3703 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -288,7 +288,6 @@ static const struct ethtool_ops dsa_slave_ethtool_ops = { .get_drvinfo = dsa_slave_get_drvinfo, .nway_reset = dsa_slave_nway_reset, .get_link = dsa_slave_get_link, - .set_sg = ethtool_op_set_sg, .get_strings = dsa_slave_get_strings, .get_ethtool_stats = dsa_slave_get_ethtool_stats, .get_sset_count = dsa_slave_get_sset_count, diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index 0dc772d0d12..f2dc69cffb5 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -11,7 +11,7 @@ obj-y := route.o inetpeer.o protocol.o \ datagram.o raw.o udp.o udplite.o \ arp.o icmp.o devinet.o af_inet.o igmp.o \ fib_frontend.o fib_semantics.o fib_trie.o \ - inet_fragment.o + inet_fragment.o ping.o obj-$(CONFIG_SYSCTL) += sysctl_net_ipv4.o obj-$(CONFIG_PROC_FS) += proc.o diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 807d83c02ef..83673d23d4d 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -105,6 +105,7 @@ #include <net/tcp.h> #include <net/udp.h> #include <net/udplite.h> +#include <net/ping.h> #include <linux/skbuff.h> #include <net/sock.h> #include <net/raw.h> @@ -153,7 +154,7 @@ void inet_sock_destruct(struct sock *sk) WARN_ON(sk->sk_wmem_queued); WARN_ON(sk->sk_forward_alloc); - kfree(inet->opt); + kfree(rcu_dereference_protected(inet->inet_opt, 1)); dst_release(rcu_dereference_check(sk->sk_dst_cache, 1)); sk_refcnt_debug_dec(sk); } @@ -464,6 +465,9 @@ int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) if (addr_len < sizeof(struct sockaddr_in)) goto out; + if (addr->sin_family != AF_INET) + goto out; + chk_addr_ret = inet_addr_type(sock_net(sk), addr->sin_addr.s_addr); /* Not specified by any standard per-se, however it breaks too @@ -1008,6 +1012,14 @@ static struct inet_protosw inetsw_array[] = .flags = INET_PROTOSW_PERMANENT, }, + { + .type = SOCK_DGRAM, + .protocol = IPPROTO_ICMP, + .prot = &ping_prot, + .ops = &inet_dgram_ops, + .no_check = UDP_CSUM_DEFAULT, + .flags = INET_PROTOSW_REUSE, + }, { .type = SOCK_RAW, @@ -1103,14 +1115,19 @@ static int inet_sk_reselect_saddr(struct sock *sk) struct inet_sock *inet = inet_sk(sk); __be32 old_saddr = inet->inet_saddr; __be32 daddr = inet->inet_daddr; + struct flowi4 *fl4; struct rtable *rt; __be32 new_saddr; + struct ip_options_rcu *inet_opt; - if (inet->opt && inet->opt->srr) - daddr = inet->opt->faddr; + inet_opt = rcu_dereference_protected(inet->inet_opt, + sock_owned_by_user(sk)); + if (inet_opt && inet_opt->opt.srr) + daddr = inet_opt->opt.faddr; /* Query new route. */ - rt = ip_route_connect(daddr, 0, RT_CONN_FLAGS(sk), + fl4 = &inet->cork.fl.u.ip4; + rt = ip_route_connect(fl4, daddr, 0, RT_CONN_FLAGS(sk), sk->sk_bound_dev_if, sk->sk_protocol, inet->inet_sport, inet->inet_dport, sk, false); if (IS_ERR(rt)) @@ -1118,7 +1135,7 @@ static int inet_sk_reselect_saddr(struct sock *sk) sk_setup_caps(sk, &rt->dst); - new_saddr = rt->rt_src; + new_saddr = fl4->saddr; if (new_saddr == old_saddr) return 0; @@ -1147,6 +1164,8 @@ int inet_sk_rebuild_header(struct sock *sk) struct inet_sock *inet = inet_sk(sk); struct rtable *rt = (struct rtable *)__sk_dst_check(sk, 0); __be32 daddr; + struct ip_options_rcu *inet_opt; + struct flowi4 *fl4; int err; /* Route is OK, nothing to do. */ @@ -1154,10 +1173,14 @@ int inet_sk_rebuild_header(struct sock *sk) return 0; /* Reroute. */ + rcu_read_lock(); + inet_opt = rcu_dereference(inet->inet_opt); daddr = inet->inet_daddr; - if (inet->opt && inet->opt->srr) - daddr = inet->opt->faddr; - rt = ip_route_output_ports(sock_net(sk), sk, daddr, inet->inet_saddr, + if (inet_opt && inet_opt->opt.srr) + daddr = inet_opt->opt.faddr; + rcu_read_unlock(); + fl4 = &inet->cork.fl.u.ip4; + rt = ip_route_output_ports(sock_net(sk), fl4, sk, daddr, inet->inet_saddr, inet->inet_dport, inet->inet_sport, sk->sk_protocol, RT_CONN_FLAGS(sk), sk->sk_bound_dev_if); @@ -1186,7 +1209,7 @@ EXPORT_SYMBOL(inet_sk_rebuild_header); static int inet_gso_send_check(struct sk_buff *skb) { - struct iphdr *iph; + const struct iphdr *iph; const struct net_protocol *ops; int proto; int ihl; @@ -1293,7 +1316,7 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head, const struct net_protocol *ops; struct sk_buff **pp = NULL; struct sk_buff *p; - struct iphdr *iph; + const struct iphdr *iph; unsigned int hlen; unsigned int off; unsigned int id; @@ -1414,11 +1437,11 @@ EXPORT_SYMBOL_GPL(inet_ctl_sock_create); unsigned long snmp_fold_field(void __percpu *mib[], int offt) { unsigned long res = 0; - int i; + int i, j; for_each_possible_cpu(i) { - res += *(((unsigned long *) per_cpu_ptr(mib[0], i)) + offt); - res += *(((unsigned long *) per_cpu_ptr(mib[1], i)) + offt); + for (j = 0; j < SNMP_ARRAY_SZ; j++) + res += *(((unsigned long *) per_cpu_ptr(mib[j], i)) + offt); } return res; } @@ -1432,28 +1455,19 @@ u64 snmp_fold_field64(void __percpu *mib[], int offt, size_t syncp_offset) int cpu; for_each_possible_cpu(cpu) { - void *bhptr, *userptr; + void *bhptr; struct u64_stats_sync *syncp; - u64 v_bh, v_user; + u64 v; unsigned int start; - /* first mib used by softirq context, we must use _bh() accessors */ - bhptr = per_cpu_ptr(SNMP_STAT_BHPTR(mib), cpu); + bhptr = per_cpu_ptr(mib[0], cpu); syncp = (struct u64_stats_sync *)(bhptr + syncp_offset); do { start = u64_stats_fetch_begin_bh(syncp); - v_bh = *(((u64 *) bhptr) + offt); + v = *(((u64 *) bhptr) + offt); } while (u64_stats_fetch_retry_bh(syncp, start)); - /* second mib used in USER context */ - userptr = per_cpu_ptr(SNMP_STAT_USRPTR(mib), cpu); - syncp = (struct u64_stats_sync *)(userptr + syncp_offset); - do { - start = u64_stats_fetch_begin(syncp); - v_user = *(((u64 *) userptr) + offt); - } while (u64_stats_fetch_retry(syncp, start)); - - res += v_bh + v_user; + res += v; } return res; } @@ -1465,25 +1479,28 @@ int snmp_mib_init(void __percpu *ptr[2], size_t mibsize, size_t align) BUG_ON(ptr == NULL); ptr[0] = __alloc_percpu(mibsize, align); if (!ptr[0]) - goto err0; + return -ENOMEM; +#if SNMP_ARRAY_SZ == 2 ptr[1] = __alloc_percpu(mibsize, align); - if (!ptr[1]) - goto err1; + if (!ptr[1]) { + free_percpu(ptr[0]); + ptr[0] = NULL; + return -ENOMEM; + } +#endif return 0; -err1: - free_percpu(ptr[0]); - ptr[0] = NULL; -err0: - return -ENOMEM; } EXPORT_SYMBOL_GPL(snmp_mib_init); -void snmp_mib_free(void __percpu *ptr[2]) +void snmp_mib_free(void __percpu *ptr[SNMP_ARRAY_SZ]) { + int i; + BUG_ON(ptr == NULL); - free_percpu(ptr[0]); - free_percpu(ptr[1]); - ptr[0] = ptr[1] = NULL; + for (i = 0; i < SNMP_ARRAY_SZ; i++) { + free_percpu(ptr[i]); + ptr[i] = NULL; + } } EXPORT_SYMBOL_GPL(snmp_mib_free); @@ -1516,6 +1533,7 @@ static const struct net_protocol udp_protocol = { static const struct net_protocol icmp_protocol = { .handler = icmp_rcv, + .err_handler = ping_err, .no_policy = 1, .netns_ok = 1, }; @@ -1631,6 +1649,10 @@ static int __init inet_init(void) if (rc) goto out_unregister_udp_proto; + rc = proto_register(&ping_prot, 1); + if (rc) + goto out_unregister_raw_proto; + /* * Tell SOCKET that we are alive... */ @@ -1686,6 +1708,8 @@ static int __init inet_init(void) /* Add UDP-Lite (RFC 3828) */ udplite4_register(); + ping_init(); + /* * Set the ICMP layer up */ @@ -1716,6 +1740,8 @@ static int __init inet_init(void) rc = 0; out: return rc; +out_unregister_raw_proto: + proto_unregister(&raw_prot); out_unregister_udp_proto: proto_unregister(&udp_prot); out_unregister_tcp_proto: @@ -1740,11 +1766,15 @@ static int __init ipv4_proc_init(void) goto out_tcp; if (udp4_proc_init()) goto out_udp; + if (ping_proc_init()) + goto out_ping; if (ip_misc_proc_init()) goto out_misc; out: return rc; out_misc: + ping_proc_exit(); +out_ping: udp4_proc_exit(); out_udp: tcp4_proc_exit(); diff --git a/net/ipv4/ah4.c b/net/ipv4/ah4.c index 4286fd3cc0e..c1f4154552f 100644 --- a/net/ipv4/ah4.c +++ b/net/ipv4/ah4.c @@ -73,7 +73,7 @@ static inline struct scatterlist *ah_req_sg(struct crypto_ahash *ahash, * into IP header for icv calculation. Options are already checked * for validity, so paranoia is not required. */ -static int ip_clear_mutable_options(struct iphdr *iph, __be32 *daddr) +static int ip_clear_mutable_options(const struct iphdr *iph, __be32 *daddr) { unsigned char * optptr = (unsigned char*)(iph+1); int l = iph->ihl*4 - sizeof(struct iphdr); @@ -396,7 +396,7 @@ out: static void ah4_err(struct sk_buff *skb, u32 info) { struct net *net = dev_net(skb->dev); - struct iphdr *iph = (struct iphdr *)skb->data; + const struct iphdr *iph = (const struct iphdr *)skb->data; struct ip_auth_hdr *ah = (struct ip_auth_hdr *)(skb->data+(iph->ihl<<2)); struct xfrm_state *x; @@ -404,7 +404,8 @@ static void ah4_err(struct sk_buff *skb, u32 info) icmp_hdr(skb)->code != ICMP_FRAG_NEEDED) return; - x = xfrm_state_lookup(net, skb->mark, (xfrm_address_t *)&iph->daddr, ah->spi, IPPROTO_AH, AF_INET); + x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr, + ah->spi, IPPROTO_AH, AF_INET); if (!x) return; printk(KERN_DEBUG "pmtu discovery on SA AH/%08x/%08x\n", diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c index a0af7ea8787..2b3c23c287c 100644 --- a/net/ipv4/cipso_ipv4.c +++ b/net/ipv4/cipso_ipv4.c @@ -1857,6 +1857,11 @@ static int cipso_v4_genopt(unsigned char *buf, u32 buf_len, return CIPSO_V4_HDR_LEN + ret_val; } +static void opt_kfree_rcu(struct rcu_head *head) +{ + kfree(container_of(head, struct ip_options_rcu, rcu)); +} + /** * cipso_v4_sock_setattr - Add a CIPSO option to a socket * @sk: the socket @@ -1879,7 +1884,7 @@ int cipso_v4_sock_setattr(struct sock *sk, unsigned char *buf = NULL; u32 buf_len; u32 opt_len; - struct ip_options *opt = NULL; + struct ip_options_rcu *old, *opt = NULL; struct inet_sock *sk_inet; struct inet_connection_sock *sk_conn; @@ -1915,22 +1920,25 @@ int cipso_v4_sock_setattr(struct sock *sk, ret_val = -ENOMEM; goto socket_setattr_failure; } - memcpy(opt->__data, buf, buf_len); - opt->optlen = opt_len; - opt->cipso = sizeof(struct iphdr); + memcpy(opt->opt.__data, buf, buf_len); + opt->opt.optlen = opt_len; + opt->opt.cipso = sizeof(struct iphdr); kfree(buf); buf = NULL; sk_inet = inet_sk(sk); + + old = rcu_dereference_protected(sk_inet->inet_opt, sock_owned_by_user(sk)); if (sk_inet->is_icsk) { sk_conn = inet_csk(sk); - if (sk_inet->opt) - sk_conn->icsk_ext_hdr_len -= sk_inet->opt->optlen; - sk_conn->icsk_ext_hdr_len += opt->optlen; + if (old) + sk_conn->icsk_ext_hdr_len -= old->opt.optlen; + sk_conn->icsk_ext_hdr_len += opt->opt.optlen; sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie); } - opt = xchg(&sk_inet->opt, opt); - kfree(opt); + rcu_assign_pointer(sk_inet->inet_opt, opt); + if (old) + call_rcu(&old->rcu, opt_kfree_rcu); return 0; @@ -1960,7 +1968,7 @@ int cipso_v4_req_setattr(struct request_sock *req, unsigned char *buf = NULL; u32 buf_len; u32 opt_len; - struct ip_options *opt = NULL; + struct ip_options_rcu *opt = NULL; struct inet_request_sock *req_inet; /* We allocate the maximum CIPSO option size here so we are probably @@ -1988,15 +1996,16 @@ int cipso_v4_req_setattr(struct request_sock *req, ret_val = -ENOMEM; goto req_setattr_failure; } - memcpy(opt->__data, buf, buf_len); - opt->optlen = opt_len; - opt->cipso = sizeof(struct iphdr); + memcpy(opt->opt.__data, buf, buf_len); + opt->opt.optlen = opt_len; + opt->opt.cipso = sizeof(struct iphdr); kfree(buf); buf = NULL; req_inet = inet_rsk(req); opt = xchg(&req_inet->opt, opt); - kfree(opt); + if (opt) + call_rcu(&opt->rcu, opt_kfree_rcu); return 0; @@ -2016,34 +2025,34 @@ req_setattr_failure: * values on failure. * */ -static int cipso_v4_delopt(struct ip_options **opt_ptr) +static int cipso_v4_delopt(struct ip_options_rcu **opt_ptr) { int hdr_delta = 0; - struct ip_options *opt = *opt_ptr; + struct ip_options_rcu *opt = *opt_ptr; - if (opt->srr || opt->rr || opt->ts || opt->router_alert) { + if (opt->opt.srr || opt->opt.rr || opt->opt.ts || opt->opt.router_alert) { u8 cipso_len; u8 cipso_off; unsigned char *cipso_ptr; int iter; int optlen_new; - cipso_off = opt->cipso - sizeof(struct iphdr); - cipso_ptr = &opt->__data[cipso_off]; + cipso_off = opt->opt.cipso - sizeof(struct iphdr); + cipso_ptr = &opt->opt.__data[cipso_off]; cipso_len = cipso_ptr[1]; - if (opt->srr > opt->cipso) - opt->srr -= cipso_len; - if (opt->rr > opt->cipso) - opt->rr -= cipso_len; - if (opt->ts > opt->cipso) - opt->ts -= cipso_len; - if (opt->router_alert > opt->cipso) - opt->router_alert -= cipso_len; - opt->cipso = 0; + if (opt->opt.srr > opt->opt.cipso) + opt->opt.srr -= cipso_len; + if (opt->opt.rr > opt->opt.cipso) + opt->opt.rr -= cipso_len; + if (opt->opt.ts > opt->opt.cipso) + opt->opt.ts -= cipso_len; + if (opt->opt.router_alert > opt->opt.cipso) + opt->opt.router_alert -= cipso_len; + opt->opt.cipso = 0; memmove(cipso_ptr, cipso_ptr + cipso_len, - opt->optlen - cipso_off - cipso_len); + opt->opt.optlen - cipso_off - cipso_len); /* determining the new total option length is tricky because of * the padding necessary, the only thing i can think to do at @@ -2052,21 +2061,21 @@ static int cipso_v4_delopt(struct ip_options **opt_ptr) * from there we can determine the new total option length */ iter = 0; optlen_new = 0; - while (iter < opt->optlen) - if (opt->__data[iter] != IPOPT_NOP) { - iter += opt->__data[iter + 1]; + while (iter < opt->opt.optlen) + if (opt->opt.__data[iter] != IPOPT_NOP) { + iter += opt->opt.__data[iter + 1]; optlen_new = iter; } else iter++; - hdr_delta = opt->optlen; - opt->optlen = (optlen_new + 3) & ~3; - hdr_delta -= opt->optlen; + hdr_delta = opt->opt.optlen; + opt->opt.optlen = (optlen_new + 3) & ~3; + hdr_delta -= opt->opt.optlen; } else { /* only the cipso option was present on the socket so we can * remove the entire option struct */ *opt_ptr = NULL; - hdr_delta = opt->optlen; - kfree(opt); + hdr_delta = opt->opt.optlen; + call_rcu(&opt->rcu, opt_kfree_rcu); } return hdr_delta; @@ -2083,15 +2092,15 @@ static int cipso_v4_delopt(struct ip_options **opt_ptr) void cipso_v4_sock_delattr(struct sock *sk) { int hdr_delta; - struct ip_options *opt; + struct ip_options_rcu *opt; struct inet_sock *sk_inet; sk_inet = inet_sk(sk); - opt = sk_inet->opt; - if (opt == NULL || opt->cipso == 0) + opt = rcu_dereference_protected(sk_inet->inet_opt, 1); + if (opt == NULL || opt->opt.cipso == 0) return; - hdr_delta = cipso_v4_delopt(&sk_inet->opt); + hdr_delta = cipso_v4_delopt(&sk_inet->inet_opt); if (sk_inet->is_icsk && hdr_delta > 0) { struct inet_connection_sock *sk_conn = inet_csk(sk); sk_conn->icsk_ext_hdr_len -= hdr_delta; @@ -2109,12 +2118,12 @@ void cipso_v4_sock_delattr(struct sock *sk) */ void cipso_v4_req_delattr(struct request_sock *req) { - struct ip_options *opt; + struct ip_options_rcu *opt; struct inet_request_sock *req_inet; req_inet = inet_rsk(req); opt = req_inet->opt; - if (opt == NULL || opt->cipso == 0) + if (opt == NULL || opt->opt.cipso == 0) return; cipso_v4_delopt(&req_inet->opt); @@ -2184,14 +2193,18 @@ getattr_return: */ int cipso_v4_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr) { - struct ip_options *opt; + struct ip_options_rcu *opt; + int res = -ENOMSG; - opt = inet_sk(sk)->opt; - if (opt == NULL || opt->cipso == 0) - return -ENOMSG; - - return cipso_v4_getattr(opt->__data + opt->cipso - sizeof(struct iphdr), - secattr); + rcu_read_lock(); + opt = rcu_dereference(inet_sk(sk)->inet_opt); + if (opt && opt->opt.cipso) + res = cipso_v4_getattr(opt->opt.__data + + opt->opt.cipso - + sizeof(struct iphdr), + secattr); + rcu_read_unlock(); + return res; } /** diff --git a/net/ipv4/datagram.c b/net/ipv4/datagram.c index 85bd24ca4f6..424fafbc8cb 100644 --- a/net/ipv4/datagram.c +++ b/net/ipv4/datagram.c @@ -24,6 +24,7 @@ int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) { struct inet_sock *inet = inet_sk(sk); struct sockaddr_in *usin = (struct sockaddr_in *) uaddr; + struct flowi4 *fl4; struct rtable *rt; __be32 saddr; int oif; @@ -38,6 +39,8 @@ int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) sk_dst_reset(sk); + lock_sock(sk); + oif = sk->sk_bound_dev_if; saddr = inet->inet_saddr; if (ipv4_is_multicast(usin->sin_addr.s_addr)) { @@ -46,7 +49,8 @@ int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) if (!saddr) saddr = inet->mc_addr; } - rt = ip_route_connect(usin->sin_addr.s_addr, saddr, + fl4 = &inet->cork.fl.u.ip4; + rt = ip_route_connect(fl4, usin->sin_addr.s_addr, saddr, RT_CONN_FLAGS(sk), oif, sk->sk_protocol, inet->inet_sport, usin->sin_port, sk, true); @@ -54,26 +58,30 @@ int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) err = PTR_ERR(rt); if (err == -ENETUNREACH) IP_INC_STATS_BH(sock_net(sk), IPSTATS_MIB_OUTNOROUTES); - return err; + goto out; } if ((rt->rt_flags & RTCF_BROADCAST) && !sock_flag(sk, SOCK_BROADCAST)) { ip_rt_put(rt); - return -EACCES; + err = -EACCES; + goto out; } if (!inet->inet_saddr) - inet->inet_saddr = rt->rt_src; /* Update source address */ + inet->inet_saddr = fl4->saddr; /* Update source address */ if (!inet->inet_rcv_saddr) { - inet->inet_rcv_saddr = rt->rt_src; + inet->inet_rcv_saddr = fl4->saddr; if (sk->sk_prot->rehash) sk->sk_prot->rehash(sk); } - inet->inet_daddr = rt->rt_dst; + inet->inet_daddr = fl4->daddr; inet->inet_dport = usin->sin_port; sk->sk_state = TCP_ESTABLISHED; inet->inet_id = jiffies; sk_dst_set(sk, &rt->dst); - return 0; + err = 0; +out: + release_sock(sk); + return err; } EXPORT_SYMBOL(ip4_datagram_connect); diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index acf553f95b5..37b3c188d8b 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -1203,7 +1203,6 @@ static int inetdev_event(struct notifier_block *this, unsigned long event, break; /* fall through */ case NETDEV_NOTIFY_PEERS: - case NETDEV_BONDING_FAILOVER: /* Send gratuitous ARP to notify of link change */ inetdev_send_gratuitous_arp(dev, in_dev); break; @@ -1370,7 +1369,7 @@ errout: static size_t inet_get_link_af_size(const struct net_device *dev) { - struct in_device *in_dev = __in_dev_get_rtnl(dev); + struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); if (!in_dev) return 0; @@ -1380,7 +1379,7 @@ static size_t inet_get_link_af_size(const struct net_device *dev) static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev) { - struct in_device *in_dev = __in_dev_get_rtnl(dev); + struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); struct nlattr *nla; int i; @@ -1681,7 +1680,7 @@ static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf) return; cnf->sysctl = NULL; - unregister_sysctl_table(t->sysctl_header); + unregister_net_sysctl_table(t->sysctl_header); kfree(t->dev_name); kfree(t); } @@ -1834,8 +1833,8 @@ void __init devinet_init(void) rtnl_af_register(&inet_af_ops); - rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL); - rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL); - rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr); + rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, NULL); + rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, NULL); + rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, NULL); } diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index 03f994bcf7d..a5b413416da 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c @@ -276,7 +276,7 @@ error: static int esp_input_done2(struct sk_buff *skb, int err) { - struct iphdr *iph; + const struct iphdr *iph; struct xfrm_state *x = xfrm_input_state(skb); struct esp_data *esp = x->data; struct crypto_aead *aead = esp->aead; @@ -484,7 +484,7 @@ static u32 esp4_get_mtu(struct xfrm_state *x, int mtu) static void esp4_err(struct sk_buff *skb, u32 info) { struct net *net = dev_net(skb->dev); - struct iphdr *iph = (struct iphdr *)skb->data; + const struct iphdr *iph = (const struct iphdr *)skb->data; struct ip_esp_hdr *esph = (struct ip_esp_hdr *)(skb->data+(iph->ihl<<2)); struct xfrm_state *x; @@ -492,7 +492,8 @@ static void esp4_err(struct sk_buff *skb, u32 info) icmp_hdr(skb)->code != ICMP_FRAG_NEEDED) return; - x = xfrm_state_lookup(net, skb->mark, (xfrm_address_t *)&iph->daddr, esph->spi, IPPROTO_ESP, AF_INET); + x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr, + esph->spi, IPPROTO_ESP, AF_INET); if (!x) return; NETDEBUG(KERN_DEBUG "pmtu discovery on SA ESP/%08x/%08x\n", diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 22524716fe7..92fc5f69f5d 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -1124,9 +1124,9 @@ static struct pernet_operations fib_net_ops = { void __init ip_fib_init(void) { - rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL); - rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL); - rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib); + rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL, NULL); + rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL, NULL); + rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib, NULL); register_pernet_subsys(&fib_net_ops); register_netdevice_notifier(&fib_netdev_notifier); diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 641a5a2a9f9..33e2c35b74b 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -141,18 +141,8 @@ const struct fib_prop fib_props[RTN_MAX + 1] = { }, }; - /* Release a nexthop info record */ -static void free_fib_info_rcu(struct rcu_head *head) -{ - struct fib_info *fi = container_of(head, struct fib_info, rcu); - - if (fi->fib_metrics != (u32 *) dst_default_metrics) - kfree(fi->fib_metrics); - kfree(fi); -} - void free_fib_info(struct fib_info *fi) { if (fi->fib_dead == 0) { @@ -166,7 +156,7 @@ void free_fib_info(struct fib_info *fi) } endfor_nexthops(fi); fib_info_cnt--; release_net(fi->fib_net); - call_rcu(&fi->rcu, free_fib_info_rcu); + kfree_rcu(fi, rcu); } void fib_release_info(struct fib_info *fi) diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 9ac481a10d3..58c25ea5a5c 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -72,6 +72,7 @@ #include <linux/init.h> #include <linux/list.h> #include <linux/slab.h> +#include <linux/prefetch.h> #include <net/net_namespace.h> #include <net/ip.h> #include <net/protocol.h> @@ -369,14 +370,9 @@ static inline void free_leaf(struct leaf *l) call_rcu_bh(&l->rcu, __leaf_free_rcu); } -static void __leaf_info_free_rcu(struct rcu_head *head) -{ - kfree(container_of(head, struct leaf_info, rcu)); -} - static inline void free_leaf_info(struct leaf_info *leaf) { - call_rcu(&leaf->rcu, __leaf_info_free_rcu); + kfree_rcu(leaf, rcu); } static struct tnode *tnode_alloc(size_t size) @@ -1998,9 +1994,6 @@ struct fib_table *fib_trie_table(u32 id) t = (struct trie *) tb->tb_data; memset(t, 0, sizeof(*t)); - if (id == RT_TABLE_LOCAL) - pr_info("IPv4 FIB: Using LC-trie version %s\n", VERSION); - return tb; } diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index e5f8a71d3a2..5395e45dcce 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -83,6 +83,7 @@ #include <net/tcp.h> #include <net/udp.h> #include <net/raw.h> +#include <net/ping.h> #include <linux/skbuff.h> #include <net/sock.h> #include <linux/errno.h> @@ -108,8 +109,7 @@ struct icmp_bxm { __be32 times[3]; } data; int head_len; - struct ip_options replyopts; - unsigned char optbuf[40]; + struct ip_options_data replyopts; }; /* An array of errno for error messages from dest unreach. */ @@ -234,7 +234,7 @@ static inline void icmp_xmit_unlock(struct sock *sk) */ static inline bool icmpv4_xrlim_allow(struct net *net, struct rtable *rt, - int type, int code) + struct flowi4 *fl4, int type, int code) { struct dst_entry *dst = &rt->dst; bool rc = true; @@ -253,7 +253,7 @@ static inline bool icmpv4_xrlim_allow(struct net *net, struct rtable *rt, /* Limit if icmp type is enabled in ratemask. */ if ((1 << type) & net->ipv4.sysctl_icmp_ratemask) { if (!rt->peer) - rt_bind_peer(rt, 1); + rt_bind_peer(rt, fl4->daddr, 1); rc = inet_peer_xrlim_allow(rt->peer, net->ipv4.sysctl_icmp_ratelimit); } @@ -291,13 +291,14 @@ static int icmp_glue_bits(void *from, char *to, int offset, int len, int odd, } static void icmp_push_reply(struct icmp_bxm *icmp_param, + struct flowi4 *fl4, struct ipcm_cookie *ipc, struct rtable **rt) { struct sock *sk; struct sk_buff *skb; sk = icmp_sk(dev_net((*rt)->dst.dev)); - if (ip_append_data(sk, icmp_glue_bits, icmp_param, + if (ip_append_data(sk, fl4, icmp_glue_bits, icmp_param, icmp_param->data_len+icmp_param->head_len, icmp_param->head_len, ipc, rt, MSG_DONTWAIT) < 0) { @@ -316,7 +317,7 @@ static void icmp_push_reply(struct icmp_bxm *icmp_param, icmp_param->head_len, csum); icmph->checksum = csum_fold(csum); skb->ip_summed = CHECKSUM_NONE; - ip_push_pending_frames(sk); + ip_push_pending_frames(sk, fl4); } } @@ -329,11 +330,12 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) struct ipcm_cookie ipc; struct rtable *rt = skb_rtable(skb); struct net *net = dev_net(rt->dst.dev); + struct flowi4 fl4; struct sock *sk; struct inet_sock *inet; __be32 daddr; - if (ip_options_echo(&icmp_param->replyopts, skb)) + if (ip_options_echo(&icmp_param->replyopts.opt.opt, skb)) return; sk = icmp_xmit_lock(net); @@ -344,65 +346,60 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) icmp_param->data.icmph.checksum = 0; inet->tos = ip_hdr(skb)->tos; - daddr = ipc.addr = rt->rt_src; + daddr = ipc.addr = ip_hdr(skb)->saddr; ipc.opt = NULL; ipc.tx_flags = 0; - if (icmp_param->replyopts.optlen) { - ipc.opt = &icmp_param->replyopts; - if (ipc.opt->srr) - daddr = icmp_param->replyopts.faddr; + if (icmp_param->replyopts.opt.opt.optlen) { + ipc.opt = &icmp_param->replyopts.opt; + if (ipc.opt->opt.srr) + daddr = icmp_param->replyopts.opt.opt.faddr; } - { - struct flowi4 fl4 = { - .daddr = daddr, - .saddr = rt->rt_spec_dst, - .flowi4_tos = RT_TOS(ip_hdr(skb)->tos), - .flowi4_proto = IPPROTO_ICMP, - }; - security_skb_classify_flow(skb, flowi4_to_flowi(&fl4)); - rt = ip_route_output_key(net, &fl4); - if (IS_ERR(rt)) - goto out_unlock; - } - if (icmpv4_xrlim_allow(net, rt, icmp_param->data.icmph.type, + memset(&fl4, 0, sizeof(fl4)); + fl4.daddr = daddr; + fl4.saddr = rt->rt_spec_dst; + fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos); + fl4.flowi4_proto = IPPROTO_ICMP; + security_skb_classify_flow(skb, flowi4_to_flowi(&fl4)); + rt = ip_route_output_key(net, &fl4); + if (IS_ERR(rt)) + goto out_unlock; + if (icmpv4_xrlim_allow(net, rt, &fl4, icmp_param->data.icmph.type, icmp_param->data.icmph.code)) - icmp_push_reply(icmp_param, &ipc, &rt); + icmp_push_reply(icmp_param, &fl4, &ipc, &rt); ip_rt_put(rt); out_unlock: icmp_xmit_unlock(sk); } -static struct rtable *icmp_route_lookup(struct net *net, struct sk_buff *skb_in, - struct iphdr *iph, +static struct rtable *icmp_route_lookup(struct net *net, + struct flowi4 *fl4, + struct sk_buff *skb_in, + const struct iphdr *iph, __be32 saddr, u8 tos, int type, int code, struct icmp_bxm *param) { - struct flowi4 fl4 = { - .daddr = (param->replyopts.srr ? - param->replyopts.faddr : iph->saddr), - .saddr = saddr, - .flowi4_tos = RT_TOS(tos), - .flowi4_proto = IPPROTO_ICMP, - .fl4_icmp_type = type, - .fl4_icmp_code = code, - }; struct rtable *rt, *rt2; int err; - security_skb_classify_flow(skb_in, flowi4_to_flowi(&fl4)); - rt = __ip_route_output_key(net, &fl4); + memset(fl4, 0, sizeof(*fl4)); + fl4->daddr = (param->replyopts.opt.opt.srr ? + param->replyopts.opt.opt.faddr : iph->saddr); + fl4->saddr = saddr; + fl4->flowi4_tos = RT_TOS(tos); + fl4->flowi4_proto = IPPROTO_ICMP; + fl4->fl4_icmp_type = type; + fl4->fl4_icmp_code = code; + security_skb_classify_flow(skb_in, flowi4_to_flowi(fl4)); + rt = __ip_route_output_key(net, fl4); if (IS_ERR(rt)) return rt; /* No need to clone since we're just using its address. */ rt2 = rt; - if (!fl4.saddr) - fl4.saddr = rt->rt_src; - rt = (struct rtable *) xfrm_lookup(net, &rt->dst, - flowi4_to_flowi(&fl4), NULL, 0); + flowi4_to_flowi(fl4), NULL, 0); if (!IS_ERR(rt)) { if (rt != rt2) return rt; @@ -411,19 +408,19 @@ static struct rtable *icmp_route_lookup(struct net *net, struct sk_buff *skb_in, } else return rt; - err = xfrm_decode_session_reverse(skb_in, flowi4_to_flowi(&fl4), AF_INET); + err = xfrm_decode_session_reverse(skb_in, flowi4_to_flowi(fl4), AF_INET); if (err) goto relookup_failed; - if (inet_addr_type(net, fl4.saddr) == RTN_LOCAL) { - rt2 = __ip_route_output_key(net, &fl4); + if (inet_addr_type(net, fl4->saddr) == RTN_LOCAL) { + rt2 = __ip_route_output_key(net, fl4); if (IS_ERR(rt2)) err = PTR_ERR(rt2); } else { struct flowi4 fl4_2 = {}; unsigned long orefdst; - fl4_2.daddr = fl4.saddr; + fl4_2.daddr = fl4->saddr; rt2 = ip_route_output_key(net, &fl4_2); if (IS_ERR(rt2)) { err = PTR_ERR(rt2); @@ -431,7 +428,7 @@ static struct rtable *icmp_route_lookup(struct net *net, struct sk_buff *skb_in, } /* Ugh! */ orefdst = skb_in->_skb_refdst; /* save old refdst */ - err = ip_route_input(skb_in, fl4.daddr, fl4.saddr, + err = ip_route_input(skb_in, fl4->daddr, fl4->saddr, RT_TOS(tos), rt2->dst.dev); dst_release(&rt2->dst); @@ -443,7 +440,7 @@ static struct rtable *icmp_route_lookup(struct net *net, struct sk_buff *skb_in, goto relookup_failed; rt2 = (struct rtable *) xfrm_lookup(net, &rt2->dst, - flowi4_to_flowi(&fl4), NULL, + flowi4_to_flowi(fl4), NULL, XFRM_LOOKUP_ICMP); if (!IS_ERR(rt2)) { dst_release(&rt->dst); @@ -482,6 +479,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) struct icmp_bxm icmp_param; struct rtable *rt = skb_rtable(skb_in); struct ipcm_cookie ipc; + struct flowi4 fl4; __be32 saddr; u8 tos; struct net *net; @@ -581,7 +579,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) IPTOS_PREC_INTERNETCONTROL) : iph->tos; - if (ip_options_echo(&icmp_param.replyopts, skb_in)) + if (ip_options_echo(&icmp_param.replyopts.opt.opt, skb_in)) goto out_unlock; @@ -597,15 +595,15 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) icmp_param.offset = skb_network_offset(skb_in); inet_sk(sk)->tos = tos; ipc.addr = iph->saddr; - ipc.opt = &icmp_param.replyopts; + ipc.opt = &icmp_param.replyopts.opt; ipc.tx_flags = 0; - rt = icmp_route_lookup(net, skb_in, iph, saddr, tos, + rt = icmp_route_lookup(net, &fl4, skb_in, iph, saddr, tos, type, code, &icmp_param); if (IS_ERR(rt)) goto out_unlock; - if (!icmpv4_xrlim_allow(net, rt, type, code)) + if (!icmpv4_xrlim_allow(net, rt, &fl4, type, code)) goto ende; /* RFC says return as much as we can without exceeding 576 bytes. */ @@ -613,7 +611,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) room = dst_mtu(&rt->dst); if (room > 576) room = 576; - room -= sizeof(struct iphdr) + icmp_param.replyopts.optlen; + room -= sizeof(struct iphdr) + icmp_param.replyopts.opt.opt.optlen; room -= sizeof(struct icmphdr); icmp_param.data_len = skb_in->len - icmp_param.offset; @@ -621,7 +619,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) icmp_param.data_len = room; icmp_param.head_len = sizeof(struct icmphdr); - icmp_push_reply(&icmp_param, &ipc, &rt); + icmp_push_reply(&icmp_param, &fl4, &ipc, &rt); ende: ip_rt_put(rt); out_unlock: @@ -637,7 +635,7 @@ EXPORT_SYMBOL(icmp_send); static void icmp_unreach(struct sk_buff *skb) { - struct iphdr *iph; + const struct iphdr *iph; struct icmphdr *icmph; int hash, protocol; const struct net_protocol *ipprot; @@ -656,7 +654,7 @@ static void icmp_unreach(struct sk_buff *skb) goto out_err; icmph = icmp_hdr(skb); - iph = (struct iphdr *)skb->data; + iph = (const struct iphdr *)skb->data; if (iph->ihl < 5) /* Mangled header, drop. */ goto out_err; @@ -729,7 +727,7 @@ static void icmp_unreach(struct sk_buff *skb) if (!pskb_may_pull(skb, iph->ihl * 4 + 8)) goto out; - iph = (struct iphdr *)skb->data; + iph = (const struct iphdr *)skb->data; protocol = iph->protocol; /* @@ -758,7 +756,7 @@ out_err: static void icmp_redirect(struct sk_buff *skb) { - struct iphdr *iph; + const struct iphdr *iph; if (skb->len < sizeof(struct iphdr)) goto out_err; @@ -769,7 +767,7 @@ static void icmp_redirect(struct sk_buff *skb) if (!pskb_may_pull(skb, sizeof(struct iphdr))) goto out; - iph = (struct iphdr *)skb->data; + iph = (const struct iphdr *)skb->data; switch (icmp_hdr(skb)->code & 7) { case ICMP_REDIR_NET: @@ -784,6 +782,15 @@ static void icmp_redirect(struct sk_buff *skb) iph->saddr, skb->dev); break; } + + /* Ping wants to see redirects. + * Let's pretend they are errors of sorts... */ + if (iph->protocol == IPPROTO_ICMP && + iph->ihl >= 5 && + pskb_may_pull(skb, (iph->ihl<<2)+8)) { + ping_err(skb, icmp_hdr(skb)->un.gateway); + } + out: return; out_err: @@ -933,12 +940,12 @@ static void icmp_address_reply(struct sk_buff *skb) BUG_ON(mp == NULL); for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { if (*mp == ifa->ifa_mask && - inet_ifa_match(rt->rt_src, ifa)) + inet_ifa_match(ip_hdr(skb)->saddr, ifa)) break; } if (!ifa && net_ratelimit()) { printk(KERN_INFO "Wrong address mask %pI4 from %s/%pI4\n", - mp, dev->name, &rt->rt_src); + mp, dev->name, &ip_hdr(skb)->saddr); } } } @@ -1044,7 +1051,7 @@ error: */ static const struct icmp_control icmp_pointers[NR_ICMP_TYPES + 1] = { [ICMP_ECHOREPLY] = { - .handler = icmp_discard, + .handler = ping_rcv, }, [1] = { .handler = icmp_discard, diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 1fd3d9ce839..f1d27f6c935 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -149,17 +149,11 @@ static void ip_mc_clear_src(struct ip_mc_list *pmc); static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode, int sfcount, __be32 *psfsrc, int delta); - -static void ip_mc_list_reclaim(struct rcu_head *head) -{ - kfree(container_of(head, struct ip_mc_list, rcu)); -} - static void ip_ma_put(struct ip_mc_list *im) { if (atomic_dec_and_test(&im->refcnt)) { in_dev_put(im->interface); - call_rcu(&im->rcu, ip_mc_list_reclaim); + kfree_rcu(im, rcu); } } @@ -309,6 +303,7 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size) struct iphdr *pip; struct igmpv3_report *pig; struct net *net = dev_net(dev); + struct flowi4 fl4; while (1) { skb = alloc_skb(size + LL_ALLOCATED_SPACE(dev), @@ -321,18 +316,13 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size) } igmp_skb_size(skb) = size; - rt = ip_route_output_ports(net, NULL, IGMPV3_ALL_MCR, 0, + rt = ip_route_output_ports(net, &fl4, NULL, IGMPV3_ALL_MCR, 0, 0, 0, IPPROTO_IGMP, 0, dev->ifindex); if (IS_ERR(rt)) { kfree_skb(skb); return NULL; } - if (rt->rt_src == 0) { - kfree_skb(skb); - ip_rt_put(rt); - return NULL; - } skb_dst_set(skb, &rt->dst); skb->dev = dev; @@ -348,8 +338,8 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size) pip->tos = 0xc0; pip->frag_off = htons(IP_DF); pip->ttl = 1; - pip->daddr = rt->rt_dst; - pip->saddr = rt->rt_src; + pip->daddr = fl4.daddr; + pip->saddr = fl4.saddr; pip->protocol = IPPROTO_IGMP; pip->tot_len = 0; /* filled in later */ ip_select_ident(pip, &rt->dst, NULL); @@ -655,6 +645,7 @@ static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc, struct net_device *dev = in_dev->dev; struct net *net = dev_net(dev); __be32 group = pmc ? pmc->multiaddr : 0; + struct flowi4 fl4; __be32 dst; if (type == IGMPV3_HOST_MEMBERSHIP_REPORT) @@ -664,17 +655,12 @@ static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc, else dst = group; - rt = ip_route_output_ports(net, NULL, dst, 0, + rt = ip_route_output_ports(net, &fl4, NULL, dst, 0, 0, 0, IPPROTO_IGMP, 0, dev->ifindex); if (IS_ERR(rt)) return -1; - if (rt->rt_src == 0) { - ip_rt_put(rt); - return -1; - } - skb = alloc_skb(IGMP_SIZE+LL_ALLOCATED_SPACE(dev), GFP_ATOMIC); if (skb == NULL) { ip_rt_put(rt); @@ -695,7 +681,7 @@ static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc, iph->frag_off = htons(IP_DF); iph->ttl = 1; iph->daddr = dst; - iph->saddr = rt->rt_src; + iph->saddr = fl4.saddr; iph->protocol = IPPROTO_IGMP; ip_select_ident(iph, &rt->dst, NULL); ((u8*)&iph[1])[0] = IPOPT_RA; @@ -1169,20 +1155,18 @@ static void igmp_group_dropped(struct ip_mc_list *im) if (!in_dev->dead) { if (IGMP_V1_SEEN(in_dev)) - goto done; + return; if (IGMP_V2_SEEN(in_dev)) { if (reporter) igmp_send_report(in_dev, im, IGMP_HOST_LEAVE_MESSAGE); - goto done; + return; } /* IGMPv3 */ igmpv3_add_delrec(in_dev, im); igmp_ifc_event(in_dev); } -done: #endif - ip_mc_clear_src(im); } static void igmp_group_added(struct ip_mc_list *im) @@ -1319,6 +1303,7 @@ void ip_mc_dec_group(struct in_device *in_dev, __be32 addr) *ip = i->next_rcu; in_dev->mc_count--; igmp_group_dropped(i); + ip_mc_clear_src(i); if (!in_dev->dead) ip_rt_multicast_event(in_dev); @@ -1428,7 +1413,8 @@ void ip_mc_destroy_dev(struct in_device *in_dev) in_dev->mc_list = i->next_rcu; in_dev->mc_count--; - igmp_group_dropped(i); + /* We've dropped the groups in ip_mc_down already */ + ip_mc_clear_src(i); ip_ma_put(i); } } @@ -1836,12 +1822,6 @@ done: } EXPORT_SYMBOL(ip_mc_join_group); -static void ip_sf_socklist_reclaim(struct rcu_head *rp) -{ - kfree(container_of(rp, struct ip_sf_socklist, rcu)); - /* sk_omem_alloc should have been decreased by the caller*/ -} - static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml, struct in_device *in_dev) { @@ -1858,18 +1838,10 @@ static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml, rcu_assign_pointer(iml->sflist, NULL); /* decrease mem now to avoid the memleak warning */ atomic_sub(IP_SFLSIZE(psf->sl_max), &sk->sk_omem_alloc); - call_rcu(&psf->rcu, ip_sf_socklist_reclaim); + kfree_rcu(psf, rcu); return err; } - -static void ip_mc_socklist_reclaim(struct rcu_head *rp) -{ - kfree(container_of(rp, struct ip_mc_socklist, rcu)); - /* sk_omem_alloc should have been decreased by the caller*/ -} - - /* * Ask a socket to leave a group. */ @@ -1909,7 +1881,7 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) rtnl_unlock(); /* decrease mem now to avoid the memleak warning */ atomic_sub(sizeof(*iml), &sk->sk_omem_alloc); - call_rcu(&iml->rcu, ip_mc_socklist_reclaim); + kfree_rcu(iml, rcu); return 0; } if (!in_dev) @@ -2026,7 +1998,7 @@ int ip_mc_source(int add, int omode, struct sock *sk, struct newpsl->sl_addr[i] = psl->sl_addr[i]; /* decrease mem now to avoid the memleak warning */ atomic_sub(IP_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc); - call_rcu(&psl->rcu, ip_sf_socklist_reclaim); + kfree_rcu(psl, rcu); } rcu_assign_pointer(pmc->sflist, newpsl); psl = newpsl; @@ -2127,7 +2099,7 @@ int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex) psl->sl_count, psl->sl_addr, 0); /* decrease mem now to avoid the memleak warning */ atomic_sub(IP_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc); - call_rcu(&psl->rcu, ip_sf_socklist_reclaim); + kfree_rcu(psl, rcu); } else (void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode, 0, NULL, 0); @@ -2324,7 +2296,7 @@ void ip_mc_drop_socket(struct sock *sk) ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr); /* decrease mem now to avoid the memleak warning */ atomic_sub(sizeof(*iml), &sk->sk_omem_alloc); - call_rcu(&iml->rcu, ip_mc_socklist_reclaim); + kfree_rcu(iml, rcu); } rtnl_unlock(); } diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 8514db54a7f..c14d88ad348 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -33,7 +33,7 @@ EXPORT_SYMBOL(inet_csk_timer_bug_msg); * This struct holds the first and last local port number. */ struct local_ports sysctl_local_ports __read_mostly = { - .lock = SEQLOCK_UNLOCKED, + .lock = __SEQLOCK_UNLOCKED(sysctl_local_ports.lock), .range = { 32768, 61000 }, }; @@ -350,24 +350,24 @@ void inet_csk_reset_keepalive_timer(struct sock *sk, unsigned long len) EXPORT_SYMBOL(inet_csk_reset_keepalive_timer); struct dst_entry *inet_csk_route_req(struct sock *sk, + struct flowi4 *fl4, const struct request_sock *req) { struct rtable *rt; const struct inet_request_sock *ireq = inet_rsk(req); - struct ip_options *opt = inet_rsk(req)->opt; + struct ip_options_rcu *opt = inet_rsk(req)->opt; struct net *net = sock_net(sk); - struct flowi4 fl4; - flowi4_init_output(&fl4, sk->sk_bound_dev_if, sk->sk_mark, + flowi4_init_output(fl4, sk->sk_bound_dev_if, sk->sk_mark, RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, sk->sk_protocol, inet_sk_flowi_flags(sk), - (opt && opt->srr) ? opt->faddr : ireq->rmt_addr, + (opt && opt->opt.srr) ? opt->opt.faddr : ireq->rmt_addr, ireq->loc_addr, ireq->rmt_port, inet_sk(sk)->inet_sport); - security_req_classify_flow(req, flowi4_to_flowi(&fl4)); - rt = ip_route_output_flow(net, &fl4, sk); + security_req_classify_flow(req, flowi4_to_flowi(fl4)); + rt = ip_route_output_flow(net, fl4, sk); if (IS_ERR(rt)) goto no_route; - if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway) + if (opt && opt->opt.is_strictroute && fl4->daddr != rt->rt_gateway) goto route_err; return &rt->dst; @@ -379,6 +379,39 @@ no_route: } EXPORT_SYMBOL_GPL(inet_csk_route_req); +struct dst_entry *inet_csk_route_child_sock(struct sock *sk, + struct sock *newsk, + const struct request_sock *req) +{ + const struct inet_request_sock *ireq = inet_rsk(req); + struct inet_sock *newinet = inet_sk(newsk); + struct ip_options_rcu *opt = ireq->opt; + struct net *net = sock_net(sk); + struct flowi4 *fl4; + struct rtable *rt; + + fl4 = &newinet->cork.fl.u.ip4; + flowi4_init_output(fl4, sk->sk_bound_dev_if, sk->sk_mark, + RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, + sk->sk_protocol, inet_sk_flowi_flags(sk), + (opt && opt->opt.srr) ? opt->opt.faddr : ireq->rmt_addr, + ireq->loc_addr, ireq->rmt_port, inet_sk(sk)->inet_sport); + security_req_classify_flow(req, flowi4_to_flowi(fl4)); + rt = ip_route_output_flow(net, fl4, sk); + if (IS_ERR(rt)) + goto no_route; + if (opt && opt->opt.is_strictroute && fl4->daddr != rt->rt_gateway) + goto route_err; + return &rt->dst; + +route_err: + ip_rt_put(rt); +no_route: + IP_INC_STATS_BH(net, IPSTATS_MIB_OUTNOROUTES); + return NULL; +} +EXPORT_SYMBOL_GPL(inet_csk_route_child_sock); + static inline u32 inet_synq_hash(const __be32 raddr, const __be16 rport, const u32 rnd, const u32 synq_hsize) { diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index 2ada17129fc..5ff47656fce 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -124,7 +124,7 @@ static int inet_csk_diag_fill(struct sock *sk, #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) if (r->idiag_family == AF_INET6) { - struct ipv6_pinfo *np = inet6_sk(sk); + const struct ipv6_pinfo *np = inet6_sk(sk); ipv6_addr_copy((struct in6_addr *)r->id.idiag_src, &np->rcv_saddr); @@ -871,7 +871,7 @@ static int inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) } return netlink_dump_start(idiagnl, skb, nlh, - inet_diag_dump, NULL); + inet_diag_dump, NULL, 0); } return inet_diag_get_exact(skb, nlh); diff --git a/net/ipv4/inet_lro.c b/net/ipv4/inet_lro.c index 47038cb6c13..85a0f75dae6 100644 --- a/net/ipv4/inet_lro.c +++ b/net/ipv4/inet_lro.c @@ -51,8 +51,8 @@ MODULE_DESCRIPTION("Large Receive Offload (ipv4 / tcp)"); * Basic tcp checks whether packet is suitable for LRO */ -static int lro_tcp_ip_check(struct iphdr *iph, struct tcphdr *tcph, - int len, struct net_lro_desc *lro_desc) +static int lro_tcp_ip_check(const struct iphdr *iph, const struct tcphdr *tcph, + int len, const struct net_lro_desc *lro_desc) { /* check ip header: don't aggregate padded frames */ if (ntohs(iph->tot_len) != len) diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index 9df4e635fb5..dafbf2c98b2 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -54,15 +54,11 @@ * 1. Nodes may appear in the tree only with the pool lock held. * 2. Nodes may disappear from the tree only with the pool lock held * AND reference count being 0. - * 3. Nodes appears and disappears from unused node list only under - * "inet_peer_unused_lock". - * 4. Global variable peer_total is modified under the pool lock. - * 5. struct inet_peer fields modification: + * 3. Global variable peer_total is modified under the pool lock. + * 4. struct inet_peer fields modification: * avl_left, avl_right, avl_parent, avl_height: pool lock - * unused: unused node list lock * refcnt: atomically against modifications on other CPU; * usually under some other lock to prevent node disappearing - * dtime: unused node list lock * daddr: unchangeable * ip_id_count: atomic value (no lock needed) */ @@ -104,19 +100,6 @@ int inet_peer_threshold __read_mostly = 65536 + 128; /* start to throw entries m * aggressively at this stage */ int inet_peer_minttl __read_mostly = 120 * HZ; /* TTL under high load: 120 sec */ int inet_peer_maxttl __read_mostly = 10 * 60 * HZ; /* usual time to live: 10 min */ -int inet_peer_gc_mintime __read_mostly = 10 * HZ; -int inet_peer_gc_maxtime __read_mostly = 120 * HZ; - -static struct { - struct list_head list; - spinlock_t lock; -} unused_peers = { - .list = LIST_HEAD_INIT(unused_peers.list), - .lock = __SPIN_LOCK_UNLOCKED(unused_peers.lock), -}; - -static void peer_check_expire(unsigned long dummy); -static DEFINE_TIMER(peer_periodic_timer, peer_check_expire, 0, 0); /* Called from ip_output.c:ip_init */ @@ -142,23 +125,6 @@ void __init inet_initpeers(void) 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL); - /* All the timers, started at system startup tend - to synchronize. Perturb it a bit. - */ - peer_periodic_timer.expires = jiffies - + net_random() % inet_peer_gc_maxtime - + inet_peer_gc_maxtime; - add_timer(&peer_periodic_timer); -} - -/* Called with or without local BH being disabled. */ -static void unlink_from_unused(struct inet_peer *p) -{ - if (!list_empty(&p->unused)) { - spin_lock_bh(&unused_peers.lock); - list_del_init(&p->unused); - spin_unlock_bh(&unused_peers.lock); - } } static int addr_compare(const struct inetpeer_addr *a, @@ -222,11 +188,9 @@ static struct inet_peer *lookup_rcu(const struct inetpeer_addr *daddr, int cmp = addr_compare(daddr, &u->daddr); if (cmp == 0) { /* Before taking a reference, check if this entry was - * deleted, unlink_from_pool() sets refcnt=-1 to make - * distinction between an unused entry (refcnt=0) and - * a freed one. + * deleted (refcnt=-1) */ - if (unlikely(!atomic_add_unless(&u->refcnt, 1, -1))) + if (!atomic_add_unless(&u->refcnt, 1, -1)) u = NULL; return u; } @@ -353,121 +317,86 @@ static void inetpeer_free_rcu(struct rcu_head *head) kmem_cache_free(peer_cachep, container_of(head, struct inet_peer, rcu)); } -/* May be called with local BH enabled. */ static void unlink_from_pool(struct inet_peer *p, struct inet_peer_base *base, struct inet_peer __rcu **stack[PEER_MAXDEPTH]) { - int do_free; - - do_free = 0; - - write_seqlock_bh(&base->lock); - /* Check the reference counter. It was artificially incremented by 1 - * in cleanup() function to prevent sudden disappearing. If we can - * atomically (because of lockless readers) take this last reference, - * it's safe to remove the node and free it later. - * We use refcnt=-1 to alert lockless readers this entry is deleted. - */ - if (atomic_cmpxchg(&p->refcnt, 1, -1) == 1) { - struct inet_peer __rcu ***stackptr, ***delp; - if (lookup(&p->daddr, stack, base) != p) - BUG(); - delp = stackptr - 1; /* *delp[0] == p */ - if (p->avl_left == peer_avl_empty_rcu) { - *delp[0] = p->avl_right; - --stackptr; - } else { - /* look for a node to insert instead of p */ - struct inet_peer *t; - t = lookup_rightempty(p, base); - BUG_ON(rcu_deref_locked(*stackptr[-1], base) != t); - **--stackptr = t->avl_left; - /* t is removed, t->daddr > x->daddr for any - * x in p->avl_left subtree. - * Put t in the old place of p. */ - RCU_INIT_POINTER(*delp[0], t); - t->avl_left = p->avl_left; - t->avl_right = p->avl_right; - t->avl_height = p->avl_height; - BUG_ON(delp[1] != &p->avl_left); - delp[1] = &t->avl_left; /* was &p->avl_left */ - } - peer_avl_rebalance(stack, stackptr, base); - base->total--; - do_free = 1; + struct inet_peer __rcu ***stackptr, ***delp; + + if (lookup(&p->daddr, stack, base) != p) + BUG(); + delp = stackptr - 1; /* *delp[0] == p */ + if (p->avl_left == peer_avl_empty_rcu) { + *delp[0] = p->avl_right; + --stackptr; + } else { + /* look for a node to insert instead of p */ + struct inet_peer *t; + t = lookup_rightempty(p, base); + BUG_ON(rcu_deref_locked(*stackptr[-1], base) != t); + **--stackptr = t->avl_left; + /* t is removed, t->daddr > x->daddr for any + * x in p->avl_left subtree. + * Put t in the old place of p. */ + RCU_INIT_POINTER(*delp[0], t); + t->avl_left = p->avl_left; + t->avl_right = p->avl_right; + t->avl_height = p->avl_height; + BUG_ON(delp[1] != &p->avl_left); + delp[1] = &t->avl_left; /* was &p->avl_left */ } - write_sequnlock_bh(&base->lock); - - if (do_free) - call_rcu(&p->rcu, inetpeer_free_rcu); - else - /* The node is used again. Decrease the reference counter - * back. The loop "cleanup -> unlink_from_unused - * -> unlink_from_pool -> putpeer -> link_to_unused - * -> cleanup (for the same node)" - * doesn't really exist because the entry will have a - * recent deletion time and will not be cleaned again soon. - */ - inet_putpeer(p); + peer_avl_rebalance(stack, stackptr, base); + base->total--; + call_rcu(&p->rcu, inetpeer_free_rcu); } static struct inet_peer_base *family_to_base(int family) { - return (family == AF_INET ? &v4_peers : &v6_peers); + return family == AF_INET ? &v4_peers : &v6_peers; } -static struct inet_peer_base *peer_to_base(struct inet_peer *p) +/* perform garbage collect on all items stacked during a lookup */ +static int inet_peer_gc(struct inet_peer_base *base, + struct inet_peer __rcu **stack[PEER_MAXDEPTH], + struct inet_peer __rcu ***stackptr) { - return family_to_base(p->daddr.family); -} + struct inet_peer *p, *gchead = NULL; + __u32 delta, ttl; + int cnt = 0; -/* May be called with local BH enabled. */ -static int cleanup_once(unsigned long ttl, struct inet_peer __rcu **stack[PEER_MAXDEPTH]) -{ - struct inet_peer *p = NULL; - - /* Remove the first entry from the list of unused nodes. */ - spin_lock_bh(&unused_peers.lock); - if (!list_empty(&unused_peers.list)) { - __u32 delta; - - p = list_first_entry(&unused_peers.list, struct inet_peer, unused); + if (base->total >= inet_peer_threshold) + ttl = 0; /* be aggressive */ + else + ttl = inet_peer_maxttl + - (inet_peer_maxttl - inet_peer_minttl) / HZ * + base->total / inet_peer_threshold * HZ; + stackptr--; /* last stack slot is peer_avl_empty */ + while (stackptr > stack) { + stackptr--; + p = rcu_deref_locked(**stackptr, base); delta = (__u32)jiffies - p->dtime; - - if (delta < ttl) { - /* Do not prune fresh entries. */ - spin_unlock_bh(&unused_peers.lock); - return -1; + if (atomic_read(&p->refcnt) == 0 && delta >= ttl && + atomic_cmpxchg(&p->refcnt, 0, -1) == 0) { + p->gc_next = gchead; + gchead = p; } - - list_del_init(&p->unused); - - /* Grab an extra reference to prevent node disappearing - * before unlink_from_pool() call. */ - atomic_inc(&p->refcnt); } - spin_unlock_bh(&unused_peers.lock); - - if (p == NULL) - /* It means that the total number of USED entries has - * grown over inet_peer_threshold. It shouldn't really - * happen because of entry limits in route cache. */ - return -1; - - unlink_from_pool(p, peer_to_base(p), stack); - return 0; + while ((p = gchead) != NULL) { + gchead = p->gc_next; + cnt++; + unlink_from_pool(p, base, stack); + } + return cnt; } -/* Called with or without local BH being disabled. */ struct inet_peer *inet_getpeer(struct inetpeer_addr *daddr, int create) { struct inet_peer __rcu **stack[PEER_MAXDEPTH], ***stackptr; struct inet_peer_base *base = family_to_base(daddr->family); struct inet_peer *p; unsigned int sequence; - int invalidated; + int invalidated, gccnt = 0; - /* Look up for the address quickly, lockless. + /* Attempt a lockless lookup first. * Because of a concurrent writer, we might not find an existing entry. */ rcu_read_lock(); @@ -476,13 +405,8 @@ struct inet_peer *inet_getpeer(struct inetpeer_addr *daddr, int create) invalidated = read_seqretry(&base->lock, sequence); rcu_read_unlock(); - if (p) { - /* The existing node has been found. - * Remove the entry from unused list if it was there. - */ - unlink_from_unused(p); + if (p) return p; - } /* If no writer did a change during our lookup, we can return early. */ if (!create && !invalidated) @@ -492,14 +416,18 @@ struct inet_peer *inet_getpeer(struct inetpeer_addr *daddr, int create) * At least, nodes should be hot in our cache. */ write_seqlock_bh(&base->lock); +relookup: p = lookup(daddr, stack, base); if (p != peer_avl_empty) { atomic_inc(&p->refcnt); write_sequnlock_bh(&base->lock); - /* Remove the entry from unused list if it was there. */ - unlink_from_unused(p); return p; } + if (!gccnt) { + gccnt = inet_peer_gc(base, stack, stackptr); + if (gccnt && create) + goto relookup; + } p = create ? kmem_cache_alloc(peer_cachep, GFP_ATOMIC) : NULL; if (p) { p->daddr = *daddr; @@ -513,7 +441,6 @@ struct inet_peer *inet_getpeer(struct inetpeer_addr *daddr, int create) p->pmtu_expires = 0; p->pmtu_orig = 0; memset(&p->redirect_learned, 0, sizeof(p->redirect_learned)); - INIT_LIST_HEAD(&p->unused); /* Link the node. */ @@ -522,63 +449,14 @@ struct inet_peer *inet_getpeer(struct inetpeer_addr *daddr, int create) } write_sequnlock_bh(&base->lock); - if (base->total >= inet_peer_threshold) - /* Remove one less-recently-used entry. */ - cleanup_once(0, stack); - return p; } - -static int compute_total(void) -{ - return v4_peers.total + v6_peers.total; -} EXPORT_SYMBOL_GPL(inet_getpeer); -/* Called with local BH disabled. */ -static void peer_check_expire(unsigned long dummy) -{ - unsigned long now = jiffies; - int ttl, total; - struct inet_peer __rcu **stack[PEER_MAXDEPTH]; - - total = compute_total(); - if (total >= inet_peer_threshold) - ttl = inet_peer_minttl; - else - ttl = inet_peer_maxttl - - (inet_peer_maxttl - inet_peer_minttl) / HZ * - total / inet_peer_threshold * HZ; - while (!cleanup_once(ttl, stack)) { - if (jiffies != now) - break; - } - - /* Trigger the timer after inet_peer_gc_mintime .. inet_peer_gc_maxtime - * interval depending on the total number of entries (more entries, - * less interval). */ - total = compute_total(); - if (total >= inet_peer_threshold) - peer_periodic_timer.expires = jiffies + inet_peer_gc_mintime; - else - peer_periodic_timer.expires = jiffies - + inet_peer_gc_maxtime - - (inet_peer_gc_maxtime - inet_peer_gc_mintime) / HZ * - total / inet_peer_threshold * HZ; - add_timer(&peer_periodic_timer); -} - void inet_putpeer(struct inet_peer *p) { - local_bh_disable(); - - if (atomic_dec_and_lock(&p->refcnt, &unused_peers.lock)) { - list_add_tail(&p->unused, &unused_peers.list); - p->dtime = (__u32)jiffies; - spin_unlock(&unused_peers.lock); - } - - local_bh_enable(); + p->dtime = (__u32)jiffies; + atomic_dec(&p->refcnt); } EXPORT_SYMBOL_GPL(inet_putpeer); diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c index 99461f09320..3b34d1c8627 100644 --- a/net/ipv4/ip_forward.c +++ b/net/ipv4/ip_forward.c @@ -84,7 +84,7 @@ int ip_forward(struct sk_buff *skb) rt = skb_rtable(skb); - if (opt->is_strictroute && rt->rt_dst != rt->rt_gateway) + if (opt->is_strictroute && ip_hdr(skb)->daddr != rt->rt_gateway) goto sr_failed; if (unlikely(skb->len > dst_mtu(&rt->dst) && !skb_is_gso(skb) && diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index a1151b8adf3..0ad6035f636 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -77,22 +77,40 @@ struct ipq { struct inet_peer *peer; }; -#define IPFRAG_ECN_CLEAR 0x01 /* one frag had INET_ECN_NOT_ECT */ -#define IPFRAG_ECN_SET_CE 0x04 /* one frag had INET_ECN_CE */ +/* RFC 3168 support : + * We want to check ECN values of all fragments, do detect invalid combinations. + * In ipq->ecn, we store the OR value of each ip4_frag_ecn() fragment value. + */ +#define IPFRAG_ECN_NOT_ECT 0x01 /* one frag had ECN_NOT_ECT */ +#define IPFRAG_ECN_ECT_1 0x02 /* one frag had ECN_ECT_1 */ +#define IPFRAG_ECN_ECT_0 0x04 /* one frag had ECN_ECT_0 */ +#define IPFRAG_ECN_CE 0x08 /* one frag had ECN_CE */ static inline u8 ip4_frag_ecn(u8 tos) { - tos = (tos & INET_ECN_MASK) + 1; - /* - * After the last operation we have (in binary): - * INET_ECN_NOT_ECT => 001 - * INET_ECN_ECT_1 => 010 - * INET_ECN_ECT_0 => 011 - * INET_ECN_CE => 100 - */ - return (tos & 2) ? 0 : tos; + return 1 << (tos & INET_ECN_MASK); } +/* Given the OR values of all fragments, apply RFC 3168 5.3 requirements + * Value : 0xff if frame should be dropped. + * 0 or INET_ECN_CE value, to be ORed in to final iph->tos field + */ +static const u8 ip4_frag_ecn_table[16] = { + /* at least one fragment had CE, and others ECT_0 or ECT_1 */ + [IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0] = INET_ECN_CE, + [IPFRAG_ECN_CE | IPFRAG_ECN_ECT_1] = INET_ECN_CE, + [IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0 | IPFRAG_ECN_ECT_1] = INET_ECN_CE, + + /* invalid combinations : drop frame */ + [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE] = 0xff, + [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_ECT_0] = 0xff, + [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_ECT_1] = 0xff, + [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_ECT_0 | IPFRAG_ECN_ECT_1] = 0xff, + [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0] = 0xff, + [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE | IPFRAG_ECN_ECT_1] = 0xff, + [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0 | IPFRAG_ECN_ECT_1] = 0xff, +}; + static struct inet_frags ip4_frags; int ip_frag_nqueues(struct net *net) @@ -223,31 +241,30 @@ static void ip_expire(unsigned long arg) if ((qp->q.last_in & INET_FRAG_FIRST_IN) && qp->q.fragments != NULL) { struct sk_buff *head = qp->q.fragments; + const struct iphdr *iph; + int err; rcu_read_lock(); head->dev = dev_get_by_index_rcu(net, qp->iif); if (!head->dev) goto out_rcu_unlock; + /* skb dst is stale, drop it, and perform route lookup again */ + skb_dst_drop(head); + iph = ip_hdr(head); + err = ip_route_input_noref(head, iph->daddr, iph->saddr, + iph->tos, head->dev); + if (err) + goto out_rcu_unlock; + /* - * Only search router table for the head fragment, - * when defraging timeout at PRE_ROUTING HOOK. + * Only an end host needs to send an ICMP + * "Fragment Reassembly Timeout" message, per RFC792. */ - if (qp->user == IP_DEFRAG_CONNTRACK_IN && !skb_dst(head)) { - const struct iphdr *iph = ip_hdr(head); - int err = ip_route_input(head, iph->daddr, iph->saddr, - iph->tos, head->dev); - if (unlikely(err)) - goto out_rcu_unlock; - - /* - * Only an end host needs to send an ICMP - * "Fragment Reassembly Timeout" message, per RFC792. - */ - if (skb_rtable(head)->rt_type != RTN_LOCAL) - goto out_rcu_unlock; + if (qp->user == IP_DEFRAG_CONNTRACK_IN && + skb_rtable(head)->rt_type != RTN_LOCAL) + goto out_rcu_unlock; - } /* Send an ICMP "Fragment Reassembly Timeout" message. */ icmp_send(head, ICMP_TIME_EXCEEDED, ICMP_EXC_FRAGTIME, 0); @@ -525,9 +542,15 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev, int len; int ihlen; int err; + u8 ecn; ipq_kill(qp); + ecn = ip4_frag_ecn_table[qp->ecn]; + if (unlikely(ecn == 0xff)) { + err = -EINVAL; + goto out_fail; + } /* Make the one we just received the head. */ if (prev) { head = prev->next; @@ -606,17 +629,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev, iph = ip_hdr(head); iph->frag_off = 0; iph->tot_len = htons(len); - /* RFC3168 5.3 Fragmentation support - * If one fragment had INET_ECN_NOT_ECT, - * reassembled frame also has INET_ECN_NOT_ECT - * Elif one fragment had INET_ECN_CE - * reassembled frame also has INET_ECN_CE - */ - if (qp->ecn & IPFRAG_ECN_CLEAR) - iph->tos &= ~INET_ECN_MASK; - else if (qp->ecn & IPFRAG_ECN_SET_CE) - iph->tos |= INET_ECN_CE; - + iph->tos |= ecn; IP_INC_STATS_BH(net, IPSTATS_MIB_REASMOKS); qp->q.fragments = NULL; qp->q.fragments_tail = NULL; diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index da5941f18c3..8871067560d 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -413,11 +413,6 @@ static struct ip_tunnel *ipgre_tunnel_locate(struct net *net, dev_net_set(dev, net); - if (strchr(name, '%')) { - if (dev_alloc_name(dev, name) < 0) - goto failed_free; - } - nt = netdev_priv(dev); nt->parms = *parms; dev->rtnl_link_ops = &ipgre_link_ops; @@ -462,7 +457,7 @@ static void ipgre_err(struct sk_buff *skb, u32 info) by themself??? */ - struct iphdr *iph = (struct iphdr *)skb->data; + const struct iphdr *iph = (const struct iphdr *)skb->data; __be16 *p = (__be16*)(skb->data+(iph->ihl<<2)); int grehlen = (iph->ihl<<2) + 4; const int type = icmp_hdr(skb)->type; @@ -534,7 +529,7 @@ out: rcu_read_unlock(); } -static inline void ipgre_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb) +static inline void ipgre_ecn_decapsulate(const struct iphdr *iph, struct sk_buff *skb) { if (INET_ECN_is_ce(iph->tos)) { if (skb->protocol == htons(ETH_P_IP)) { @@ -546,19 +541,19 @@ static inline void ipgre_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb) } static inline u8 -ipgre_ecn_encapsulate(u8 tos, struct iphdr *old_iph, struct sk_buff *skb) +ipgre_ecn_encapsulate(u8 tos, const struct iphdr *old_iph, struct sk_buff *skb) { u8 inner = 0; if (skb->protocol == htons(ETH_P_IP)) inner = old_iph->tos; else if (skb->protocol == htons(ETH_P_IPV6)) - inner = ipv6_get_dsfield((struct ipv6hdr *)old_iph); + inner = ipv6_get_dsfield((const struct ipv6hdr *)old_iph); return INET_ECN_encapsulate(tos, inner); } static int ipgre_rcv(struct sk_buff *skb) { - struct iphdr *iph; + const struct iphdr *iph; u8 *h; __be16 flags; __sum16 csum = 0; @@ -697,8 +692,9 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev { struct ip_tunnel *tunnel = netdev_priv(dev); struct pcpu_tstats *tstats; - struct iphdr *old_iph = ip_hdr(skb); - struct iphdr *tiph; + const struct iphdr *old_iph = ip_hdr(skb); + const struct iphdr *tiph; + struct flowi4 fl4; u8 tos; __be16 df; struct rtable *rt; /* Route to the other host */ @@ -714,7 +710,7 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev if (dev->header_ops && dev->type == ARPHRD_IPGRE) { gre_hlen = 0; - tiph = (struct iphdr *)skb->data; + tiph = (const struct iphdr *)skb->data; } else { gre_hlen = tunnel->hlen; tiph = &tunnel->parms.iph; @@ -735,14 +731,14 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev } #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) else if (skb->protocol == htons(ETH_P_IPV6)) { - struct in6_addr *addr6; + const struct in6_addr *addr6; int addr_type; struct neighbour *neigh = skb_dst(skb)->neighbour; if (neigh == NULL) goto tx_error; - addr6 = (struct in6_addr *)&neigh->primary_key; + addr6 = (const struct in6_addr *)&neigh->primary_key; addr_type = ipv6_addr_type(addr6); if (addr_type == IPV6_ADDR_ANY) { @@ -766,10 +762,10 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev if (skb->protocol == htons(ETH_P_IP)) tos = old_iph->tos; else if (skb->protocol == htons(ETH_P_IPV6)) - tos = ipv6_get_dsfield((struct ipv6hdr *)old_iph); + tos = ipv6_get_dsfield((const struct ipv6hdr *)old_iph); } - rt = ip_route_output_gre(dev_net(dev), dst, tiph->saddr, + rt = ip_route_output_gre(dev_net(dev), &fl4, dst, tiph->saddr, tunnel->parms.o_key, RT_TOS(tos), tunnel->parms.link); if (IS_ERR(rt)) { @@ -873,15 +869,15 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev iph->frag_off = df; iph->protocol = IPPROTO_GRE; iph->tos = ipgre_ecn_encapsulate(tos, old_iph, skb); - iph->daddr = rt->rt_dst; - iph->saddr = rt->rt_src; + iph->daddr = fl4.daddr; + iph->saddr = fl4.saddr; if ((iph->ttl = tiph->ttl) == 0) { if (skb->protocol == htons(ETH_P_IP)) iph->ttl = old_iph->ttl; #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) else if (skb->protocol == htons(ETH_P_IPV6)) - iph->ttl = ((struct ipv6hdr *)old_iph)->hop_limit; + iph->ttl = ((const struct ipv6hdr *)old_iph)->hop_limit; #endif else iph->ttl = ip4_dst_hoplimit(&rt->dst); @@ -927,7 +923,7 @@ static int ipgre_tunnel_bind_dev(struct net_device *dev) { struct net_device *tdev = NULL; struct ip_tunnel *tunnel; - struct iphdr *iph; + const struct iphdr *iph; int hlen = LL_MAX_HEADER; int mtu = ETH_DATA_LEN; int addend = sizeof(struct iphdr) + 4; @@ -938,12 +934,14 @@ static int ipgre_tunnel_bind_dev(struct net_device *dev) /* Guess output device to choose reasonable mtu and needed_headroom */ if (iph->daddr) { - struct rtable *rt = ip_route_output_gre(dev_net(dev), - iph->daddr, iph->saddr, - tunnel->parms.o_key, - RT_TOS(iph->tos), - tunnel->parms.link); - + struct flowi4 fl4; + struct rtable *rt; + + rt = ip_route_output_gre(dev_net(dev), &fl4, + iph->daddr, iph->saddr, + tunnel->parms.o_key, + RT_TOS(iph->tos), + tunnel->parms.link); if (!IS_ERR(rt)) { tdev = rt->dst.dev; ip_rt_put(rt); @@ -1180,7 +1178,7 @@ static int ipgre_header(struct sk_buff *skb, struct net_device *dev, static int ipgre_header_parse(const struct sk_buff *skb, unsigned char *haddr) { - struct iphdr *iph = (struct iphdr *) skb_mac_header(skb); + const struct iphdr *iph = (const struct iphdr *) skb_mac_header(skb); memcpy(haddr, &iph->saddr, 4); return 4; } @@ -1196,13 +1194,15 @@ static int ipgre_open(struct net_device *dev) struct ip_tunnel *t = netdev_priv(dev); if (ipv4_is_multicast(t->parms.iph.daddr)) { - struct rtable *rt = ip_route_output_gre(dev_net(dev), - t->parms.iph.daddr, - t->parms.iph.saddr, - t->parms.o_key, - RT_TOS(t->parms.iph.tos), - t->parms.link); - + struct flowi4 fl4; + struct rtable *rt; + + rt = ip_route_output_gre(dev_net(dev), &fl4, + t->parms.iph.daddr, + t->parms.iph.saddr, + t->parms.o_key, + RT_TOS(t->parms.iph.tos), + t->parms.link); if (IS_ERR(rt)) return -EADDRNOTAVAIL; dev = rt->dst.dev; diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index d7b2b0987a3..c8f48efc5fd 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -268,7 +268,7 @@ int ip_local_deliver(struct sk_buff *skb) static inline int ip_rcv_options(struct sk_buff *skb) { struct ip_options *opt; - struct iphdr *iph; + const struct iphdr *iph; struct net_device *dev = skb->dev; /* It looks as overkill, because not all @@ -374,7 +374,7 @@ drop: */ int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { - struct iphdr *iph; + const struct iphdr *iph; u32 len; /* When the interface is in promisc. mode, drop all the crap diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c index 2391b24e825..ec93335901d 100644 --- a/net/ipv4/ip_options.c +++ b/net/ipv4/ip_options.c @@ -14,6 +14,7 @@ #include <linux/slab.h> #include <linux/types.h> #include <asm/uaccess.h> +#include <asm/unaligned.h> #include <linux/skbuff.h> #include <linux/ip.h> #include <linux/icmp.h> @@ -36,8 +37,8 @@ * saddr is address of outgoing interface. */ -void ip_options_build(struct sk_buff * skb, struct ip_options * opt, - __be32 daddr, struct rtable *rt, int is_frag) +void ip_options_build(struct sk_buff *skb, struct ip_options *opt, + __be32 daddr, struct rtable *rt, int is_frag) { unsigned char *iph = skb_network_header(skb); @@ -50,9 +51,9 @@ void ip_options_build(struct sk_buff * skb, struct ip_options * opt, if (!is_frag) { if (opt->rr_needaddr) - ip_rt_get_source(iph+opt->rr+iph[opt->rr+2]-5, rt); + ip_rt_get_source(iph+opt->rr+iph[opt->rr+2]-5, skb, rt); if (opt->ts_needaddr) - ip_rt_get_source(iph+opt->ts+iph[opt->ts+2]-9, rt); + ip_rt_get_source(iph+opt->ts+iph[opt->ts+2]-9, skb, rt); if (opt->ts_needtime) { struct timespec tv; __be32 midtime; @@ -83,9 +84,9 @@ void ip_options_build(struct sk_buff * skb, struct ip_options * opt, * NOTE: dopt cannot point to skb. */ -int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb) +int ip_options_echo(struct ip_options *dopt, struct sk_buff *skb) { - struct ip_options *sopt; + const struct ip_options *sopt; unsigned char *sptr, *dptr; int soffset, doffset; int optlen; @@ -95,10 +96,8 @@ int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb) sopt = &(IPCB(skb)->opt); - if (sopt->optlen == 0) { - dopt->optlen = 0; + if (sopt->optlen == 0) return 0; - } sptr = skb_network_header(skb); dptr = dopt->__data; @@ -157,7 +156,7 @@ int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb) dopt->optlen += optlen; } if (sopt->srr) { - unsigned char * start = sptr+sopt->srr; + unsigned char *start = sptr+sopt->srr; __be32 faddr; optlen = start[1]; @@ -352,7 +351,7 @@ int ip_options_compile(struct net *net, goto error; } if (optptr[2] <= optlen) { - __be32 *timeptr = NULL; + unsigned char *timeptr = NULL; if (optptr[2]+3 > optptr[1]) { pp_ptr = optptr + 2; goto error; @@ -361,7 +360,7 @@ int ip_options_compile(struct net *net, case IPOPT_TS_TSONLY: opt->ts = optptr - iph; if (skb) - timeptr = (__be32*)&optptr[optptr[2]-1]; + timeptr = &optptr[optptr[2]-1]; opt->ts_needtime = 1; optptr[2] += 4; break; @@ -373,7 +372,7 @@ int ip_options_compile(struct net *net, opt->ts = optptr - iph; if (rt) { memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4); - timeptr = (__be32*)&optptr[optptr[2]+3]; + timeptr = &optptr[optptr[2]+3]; } opt->ts_needaddr = 1; opt->ts_needtime = 1; @@ -391,7 +390,7 @@ int ip_options_compile(struct net *net, if (inet_addr_type(net, addr) == RTN_UNICAST) break; if (skb) - timeptr = (__be32*)&optptr[optptr[2]+3]; + timeptr = &optptr[optptr[2]+3]; } opt->ts_needtime = 1; optptr[2] += 8; @@ -405,10 +404,10 @@ int ip_options_compile(struct net *net, } if (timeptr) { struct timespec tv; - __be32 midtime; + u32 midtime; getnstimeofday(&tv); - midtime = htonl((tv.tv_sec % 86400) * MSEC_PER_SEC + tv.tv_nsec / NSEC_PER_MSEC); - memcpy(timeptr, &midtime, sizeof(__be32)); + midtime = (tv.tv_sec % 86400) * MSEC_PER_SEC + tv.tv_nsec / NSEC_PER_MSEC; + put_unaligned_be32(midtime, timeptr); opt->is_changed = 1; } } else { @@ -499,19 +498,19 @@ void ip_options_undo(struct ip_options * opt) } } -static struct ip_options *ip_options_get_alloc(const int optlen) +static struct ip_options_rcu *ip_options_get_alloc(const int optlen) { - return kzalloc(sizeof(struct ip_options) + ((optlen + 3) & ~3), + return kzalloc(sizeof(struct ip_options_rcu) + ((optlen + 3) & ~3), GFP_KERNEL); } -static int ip_options_get_finish(struct net *net, struct ip_options **optp, - struct ip_options *opt, int optlen) +static int ip_options_get_finish(struct net *net, struct ip_options_rcu **optp, + struct ip_options_rcu *opt, int optlen) { while (optlen & 3) - opt->__data[optlen++] = IPOPT_END; - opt->optlen = optlen; - if (optlen && ip_options_compile(net, opt, NULL)) { + opt->opt.__data[optlen++] = IPOPT_END; + opt->opt.optlen = optlen; + if (optlen && ip_options_compile(net, &opt->opt, NULL)) { kfree(opt); return -EINVAL; } @@ -520,29 +519,29 @@ static int ip_options_get_finish(struct net *net, struct ip_options **optp, return 0; } -int ip_options_get_from_user(struct net *net, struct ip_options **optp, +int ip_options_get_from_user(struct net *net, struct ip_options_rcu **optp, unsigned char __user *data, int optlen) { - struct ip_options *opt = ip_options_get_alloc(optlen); + struct ip_options_rcu *opt = ip_options_get_alloc(optlen); if (!opt) return -ENOMEM; - if (optlen && copy_from_user(opt->__data, data, optlen)) { + if (optlen && copy_from_user(opt->opt.__data, data, optlen)) { kfree(opt); return -EFAULT; } return ip_options_get_finish(net, optp, opt, optlen); } -int ip_options_get(struct net *net, struct ip_options **optp, +int ip_options_get(struct net *net, struct ip_options_rcu **optp, unsigned char *data, int optlen) { - struct ip_options *opt = ip_options_get_alloc(optlen); + struct ip_options_rcu *opt = ip_options_get_alloc(optlen); if (!opt) return -ENOMEM; if (optlen) - memcpy(opt->__data, data, optlen); + memcpy(opt->opt.__data, data, optlen); return ip_options_get_finish(net, optp, opt, optlen); } @@ -555,7 +554,7 @@ void ip_forward_options(struct sk_buff *skb) if (opt->rr_needaddr) { optptr = (unsigned char *)raw + opt->rr; - ip_rt_get_source(&optptr[optptr[2]-5], rt); + ip_rt_get_source(&optptr[optptr[2]-5], skb, rt); opt->is_changed = 1; } if (opt->srr_is_hit) { @@ -569,19 +568,18 @@ void ip_forward_options(struct sk_buff *skb) ) { if (srrptr + 3 > srrspace) break; - if (memcmp(&rt->rt_dst, &optptr[srrptr-1], 4) == 0) + if (memcmp(&ip_hdr(skb)->daddr, &optptr[srrptr-1], 4) == 0) break; } if (srrptr + 3 <= srrspace) { opt->is_changed = 1; - ip_rt_get_source(&optptr[srrptr-1], rt); - ip_hdr(skb)->daddr = rt->rt_dst; + ip_rt_get_source(&optptr[srrptr-1], skb, rt); optptr[2] = srrptr+4; } else if (net_ratelimit()) printk(KERN_CRIT "ip_forward(): Argh! Destination lost!\n"); if (opt->ts_needaddr) { optptr = raw + opt->ts; - ip_rt_get_source(&optptr[optptr[2]-9], rt); + ip_rt_get_source(&optptr[optptr[2]-9], skb, rt); opt->is_changed = 1; } } @@ -603,7 +601,7 @@ int ip_options_rcv_srr(struct sk_buff *skb) unsigned long orefdst; int err; - if (!opt->srr || !rt) + if (!rt) return 0; if (skb->pkt_type != PACKET_HOST) @@ -637,7 +635,7 @@ int ip_options_rcv_srr(struct sk_buff *skb) if (rt2->rt_type != RTN_LOCAL) break; /* Superfast 8) loopback forward */ - memcpy(&iph->daddr, &optptr[srrptr-1], 4); + iph->daddr = nexthop; opt->is_changed = 1; } if (srrptr <= srrspace) { diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index bdad3d60aa8..98af3697c71 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -140,14 +140,14 @@ static inline int ip_select_ttl(struct inet_sock *inet, struct dst_entry *dst) * */ int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk, - __be32 saddr, __be32 daddr, struct ip_options *opt) + __be32 saddr, __be32 daddr, struct ip_options_rcu *opt) { struct inet_sock *inet = inet_sk(sk); struct rtable *rt = skb_rtable(skb); struct iphdr *iph; /* Build the IP header. */ - skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0)); + skb_push(skb, sizeof(struct iphdr) + (opt ? opt->opt.optlen : 0)); skb_reset_network_header(skb); iph = ip_hdr(skb); iph->version = 4; @@ -158,14 +158,14 @@ int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk, else iph->frag_off = 0; iph->ttl = ip_select_ttl(inet, &rt->dst); - iph->daddr = rt->rt_dst; - iph->saddr = rt->rt_src; + iph->daddr = (opt && opt->opt.srr ? opt->opt.faddr : daddr); + iph->saddr = saddr; iph->protocol = sk->sk_protocol; ip_select_ident(iph, &rt->dst, sk); - if (opt && opt->optlen) { - iph->ihl += opt->optlen>>2; - ip_options_build(skb, opt, daddr, rt, 0); + if (opt && opt->opt.optlen) { + iph->ihl += opt->opt.optlen>>2; + ip_options_build(skb, &opt->opt, daddr, rt, 0); } skb->priority = sk->sk_priority; @@ -312,11 +312,12 @@ int ip_output(struct sk_buff *skb) !(IPCB(skb)->flags & IPSKB_REROUTED)); } -int ip_queue_xmit(struct sk_buff *skb) +int ip_queue_xmit(struct sk_buff *skb, struct flowi *fl) { struct sock *sk = skb->sk; struct inet_sock *inet = inet_sk(sk); - struct ip_options *opt = inet->opt; + struct ip_options_rcu *inet_opt; + struct flowi4 *fl4; struct rtable *rt; struct iphdr *iph; int res; @@ -325,6 +326,8 @@ int ip_queue_xmit(struct sk_buff *skb) * f.e. by something like SCTP. */ rcu_read_lock(); + inet_opt = rcu_dereference(inet->inet_opt); + fl4 = &fl->u.ip4; rt = skb_rtable(skb); if (rt != NULL) goto packet_routed; @@ -336,14 +339,14 @@ int ip_queue_xmit(struct sk_buff *skb) /* Use correct destination address if we have options. */ daddr = inet->inet_daddr; - if(opt && opt->srr) - daddr = opt->faddr; + if (inet_opt && inet_opt->opt.srr) + daddr = inet_opt->opt.faddr; /* If this fails, retransmit mechanism of transport layer will * keep trying until route appears or the connection times * itself out. */ - rt = ip_route_output_ports(sock_net(sk), sk, + rt = ip_route_output_ports(sock_net(sk), fl4, sk, daddr, inet->inet_saddr, inet->inet_dport, inet->inet_sport, @@ -357,11 +360,11 @@ int ip_queue_xmit(struct sk_buff *skb) skb_dst_set_noref(skb, &rt->dst); packet_routed: - if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway) + if (inet_opt && inet_opt->opt.is_strictroute && fl4->daddr != rt->rt_gateway) goto no_route; /* OK, we know where to send it, allocate and build IP header. */ - skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0)); + skb_push(skb, sizeof(struct iphdr) + (inet_opt ? inet_opt->opt.optlen : 0)); skb_reset_network_header(skb); iph = ip_hdr(skb); *((__be16 *)iph) = htons((4 << 12) | (5 << 8) | (inet->tos & 0xff)); @@ -371,13 +374,13 @@ packet_routed: iph->frag_off = 0; iph->ttl = ip_select_ttl(inet, &rt->dst); iph->protocol = sk->sk_protocol; - iph->saddr = rt->rt_src; - iph->daddr = rt->rt_dst; + iph->saddr = fl4->saddr; + iph->daddr = fl4->daddr; /* Transport layer set skb->h.foo itself. */ - if (opt && opt->optlen) { - iph->ihl += opt->optlen >> 2; - ip_options_build(skb, opt, inet->inet_daddr, rt, 0); + if (inet_opt && inet_opt->opt.optlen) { + iph->ihl += inet_opt->opt.optlen >> 2; + ip_options_build(skb, &inet_opt->opt, inet->inet_daddr, rt, 0); } ip_select_ident_more(iph, &rt->dst, sk, @@ -773,7 +776,9 @@ static inline int ip_ufo_append_data(struct sock *sk, (length - transhdrlen)); } -static int __ip_append_data(struct sock *sk, struct sk_buff_head *queue, +static int __ip_append_data(struct sock *sk, + struct flowi4 *fl4, + struct sk_buff_head *queue, struct inet_cork *cork, int getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb), @@ -805,7 +810,7 @@ static int __ip_append_data(struct sock *sk, struct sk_buff_head *queue, maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen; if (cork->length + length > 0xFFFF - fragheaderlen) { - ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->inet_dport, + ip_local_error(sk, EMSGSIZE, fl4->daddr, inet->inet_dport, mtu-exthdrlen); return -EMSGSIZE; } @@ -1033,7 +1038,7 @@ static int ip_setup_cork(struct sock *sk, struct inet_cork *cork, struct ipcm_cookie *ipc, struct rtable **rtp) { struct inet_sock *inet = inet_sk(sk); - struct ip_options *opt; + struct ip_options_rcu *opt; struct rtable *rt; /* @@ -1047,7 +1052,7 @@ static int ip_setup_cork(struct sock *sk, struct inet_cork *cork, if (unlikely(cork->opt == NULL)) return -ENOBUFS; } - memcpy(cork->opt, opt, sizeof(struct ip_options) + opt->optlen); + memcpy(cork->opt, &opt->opt, sizeof(struct ip_options) + opt->opt.optlen); cork->flags |= IPCORK_OPT; cork->addr = ipc->addr; } @@ -1080,7 +1085,7 @@ static int ip_setup_cork(struct sock *sk, struct inet_cork *cork, * * LATER: length must be adjusted by pad at tail, when it is required. */ -int ip_append_data(struct sock *sk, +int ip_append_data(struct sock *sk, struct flowi4 *fl4, int getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb), void *from, int length, int transhdrlen, @@ -1094,24 +1099,25 @@ int ip_append_data(struct sock *sk, return 0; if (skb_queue_empty(&sk->sk_write_queue)) { - err = ip_setup_cork(sk, &inet->cork, ipc, rtp); + err = ip_setup_cork(sk, &inet->cork.base, ipc, rtp); if (err) return err; } else { transhdrlen = 0; } - return __ip_append_data(sk, &sk->sk_write_queue, &inet->cork, getfrag, + return __ip_append_data(sk, fl4, &sk->sk_write_queue, &inet->cork.base, getfrag, from, length, transhdrlen, flags); } -ssize_t ip_append_page(struct sock *sk, struct page *page, +ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page, int offset, size_t size, int flags) { struct inet_sock *inet = inet_sk(sk); struct sk_buff *skb; struct rtable *rt; struct ip_options *opt = NULL; + struct inet_cork *cork; int hh_len; int mtu; int len; @@ -1127,28 +1133,29 @@ ssize_t ip_append_page(struct sock *sk, struct page *page, if (skb_queue_empty(&sk->sk_write_queue)) return -EINVAL; - rt = (struct rtable *)inet->cork.dst; - if (inet->cork.flags & IPCORK_OPT) - opt = inet->cork.opt; + cork = &inet->cork.base; + rt = (struct rtable *)cork->dst; + if (cork->flags & IPCORK_OPT) + opt = cork->opt; if (!(rt->dst.dev->features&NETIF_F_SG)) return -EOPNOTSUPP; hh_len = LL_RESERVED_SPACE(rt->dst.dev); - mtu = inet->cork.fragsize; + mtu = cork->fragsize; fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0); maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen; - if (inet->cork.length + size > 0xFFFF - fragheaderlen) { - ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->inet_dport, mtu); + if (cork->length + size > 0xFFFF - fragheaderlen) { + ip_local_error(sk, EMSGSIZE, fl4->daddr, inet->inet_dport, mtu); return -EMSGSIZE; } if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL) return -EINVAL; - inet->cork.length += size; + cork->length += size; if ((size + skb->len > mtu) && (sk->sk_protocol == IPPROTO_UDP) && (rt->dst.dev->features & NETIF_F_UFO)) { @@ -1243,7 +1250,7 @@ ssize_t ip_append_page(struct sock *sk, struct page *page, return 0; error: - inet->cork.length -= size; + cork->length -= size; IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTDISCARDS); return err; } @@ -1262,6 +1269,7 @@ static void ip_cork_release(struct inet_cork *cork) * and push them out. */ struct sk_buff *__ip_make_skb(struct sock *sk, + struct flowi4 *fl4, struct sk_buff_head *queue, struct inet_cork *cork) { @@ -1319,17 +1327,18 @@ struct sk_buff *__ip_make_skb(struct sock *sk, iph = (struct iphdr *)skb->data; iph->version = 4; iph->ihl = 5; - if (opt) { - iph->ihl += opt->optlen>>2; - ip_options_build(skb, opt, cork->addr, rt, 0); - } iph->tos = inet->tos; iph->frag_off = df; ip_select_ident(iph, &rt->dst, sk); iph->ttl = ttl; iph->protocol = sk->sk_protocol; - iph->saddr = rt->rt_src; - iph->daddr = rt->rt_dst; + iph->saddr = fl4->saddr; + iph->daddr = fl4->daddr; + + if (opt) { + iph->ihl += opt->optlen>>2; + ip_options_build(skb, opt, cork->addr, rt, 0); + } skb->priority = sk->sk_priority; skb->mark = sk->sk_mark; @@ -1365,11 +1374,11 @@ int ip_send_skb(struct sk_buff *skb) return err; } -int ip_push_pending_frames(struct sock *sk) +int ip_push_pending_frames(struct sock *sk, struct flowi4 *fl4) { struct sk_buff *skb; - skb = ip_finish_skb(sk); + skb = ip_finish_skb(sk, fl4); if (!skb) return 0; @@ -1394,17 +1403,18 @@ static void __ip_flush_pending_frames(struct sock *sk, void ip_flush_pending_frames(struct sock *sk) { - __ip_flush_pending_frames(sk, &sk->sk_write_queue, &inet_sk(sk)->cork); + __ip_flush_pending_frames(sk, &sk->sk_write_queue, &inet_sk(sk)->cork.base); } struct sk_buff *ip_make_skb(struct sock *sk, + struct flowi4 *fl4, int getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb), void *from, int length, int transhdrlen, struct ipcm_cookie *ipc, struct rtable **rtp, unsigned int flags) { - struct inet_cork cork = {}; + struct inet_cork cork; struct sk_buff_head queue; int err; @@ -1413,18 +1423,21 @@ struct sk_buff *ip_make_skb(struct sock *sk, __skb_queue_head_init(&queue); + cork.flags = 0; + cork.addr = 0; + cork.opt = NULL; err = ip_setup_cork(sk, &cork, ipc, rtp); if (err) return ERR_PTR(err); - err = __ip_append_data(sk, &queue, &cork, getfrag, + err = __ip_append_data(sk, fl4, &queue, &cork, getfrag, from, length, transhdrlen, flags); if (err) { __ip_flush_pending_frames(sk, &queue, &cork); return ERR_PTR(err); } - return __ip_make_skb(sk, &queue, &cork); + return __ip_make_skb(sk, fl4, &queue, &cork); } /* @@ -1447,46 +1460,39 @@ static int ip_reply_glue_bits(void *dptr, char *to, int offset, * Should run single threaded per socket because it uses the sock * structure to pass arguments. */ -void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *arg, - unsigned int len) +void ip_send_reply(struct sock *sk, struct sk_buff *skb, __be32 daddr, + struct ip_reply_arg *arg, unsigned int len) { struct inet_sock *inet = inet_sk(sk); - struct { - struct ip_options opt; - char data[40]; - } replyopts; + struct ip_options_data replyopts; struct ipcm_cookie ipc; - __be32 daddr; + struct flowi4 fl4; struct rtable *rt = skb_rtable(skb); - if (ip_options_echo(&replyopts.opt, skb)) + if (ip_options_echo(&replyopts.opt.opt, skb)) return; - daddr = ipc.addr = rt->rt_src; + ipc.addr = daddr; ipc.opt = NULL; ipc.tx_flags = 0; - if (replyopts.opt.optlen) { + if (replyopts.opt.opt.optlen) { ipc.opt = &replyopts.opt; - if (ipc.opt->srr) - daddr = replyopts.opt.faddr; + if (replyopts.opt.opt.srr) + daddr = replyopts.opt.opt.faddr; } - { - struct flowi4 fl4; - - flowi4_init_output(&fl4, arg->bound_dev_if, 0, - RT_TOS(ip_hdr(skb)->tos), - RT_SCOPE_UNIVERSE, sk->sk_protocol, - ip_reply_arg_flowi_flags(arg), - daddr, rt->rt_spec_dst, - tcp_hdr(skb)->source, tcp_hdr(skb)->dest); - security_skb_classify_flow(skb, flowi4_to_flowi(&fl4)); - rt = ip_route_output_key(sock_net(sk), &fl4); - if (IS_ERR(rt)) - return; - } + flowi4_init_output(&fl4, arg->bound_dev_if, 0, + RT_TOS(ip_hdr(skb)->tos), + RT_SCOPE_UNIVERSE, sk->sk_protocol, + ip_reply_arg_flowi_flags(arg), + daddr, rt->rt_spec_dst, + tcp_hdr(skb)->source, tcp_hdr(skb)->dest); + security_skb_classify_flow(skb, flowi4_to_flowi(&fl4)); + rt = ip_route_output_key(sock_net(sk), &fl4); + if (IS_ERR(rt)) + return; /* And let IP do all the hard work. @@ -1499,7 +1505,7 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *ar sk->sk_priority = skb->priority; sk->sk_protocol = ip_hdr(skb)->protocol; sk->sk_bound_dev_if = arg->bound_dev_if; - ip_append_data(sk, ip_reply_glue_bits, arg->iov->iov_base, len, 0, + ip_append_data(sk, &fl4, ip_reply_glue_bits, arg->iov->iov_base, len, 0, &ipc, &rt, MSG_DONTWAIT); if ((skb = skb_peek(&sk->sk_write_queue)) != NULL) { if (arg->csumoffset >= 0) @@ -1507,7 +1513,7 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *ar arg->csumoffset) = csum_fold(csum_add(skb->csum, arg->csum)); skb->ip_summed = CHECKSUM_NONE; - ip_push_pending_frames(sk); + ip_push_pending_frames(sk, &fl4); } bh_unlock_sock(sk); diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 3948c86e59c..ab0c9efd1ef 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -131,7 +131,7 @@ static void ip_cmsg_recv_security(struct msghdr *msg, struct sk_buff *skb) static void ip_cmsg_recv_dstaddr(struct msghdr *msg, struct sk_buff *skb) { struct sockaddr_in sin; - struct iphdr *iph = ip_hdr(skb); + const struct iphdr *iph = ip_hdr(skb); __be16 *ports = (__be16 *)skb_transport_header(skb); if (skb_transport_offset(skb) + 4 > skb->len) @@ -451,6 +451,11 @@ out: } +static void opt_kfree_rcu(struct rcu_head *head) +{ + kfree(container_of(head, struct ip_options_rcu, rcu)); +} + /* * Socket option code for IP. This is the end of the line after any * TCP,UDP etc options on an IP socket. @@ -497,13 +502,16 @@ static int do_ip_setsockopt(struct sock *sk, int level, switch (optname) { case IP_OPTIONS: { - struct ip_options *opt = NULL; + struct ip_options_rcu *old, *opt = NULL; + if (optlen > 40) goto e_inval; err = ip_options_get_from_user(sock_net(sk), &opt, optval, optlen); if (err) break; + old = rcu_dereference_protected(inet->inet_opt, + sock_owned_by_user(sk)); if (inet->is_icsk) { struct inet_connection_sock *icsk = inet_csk(sk); #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) @@ -512,17 +520,18 @@ static int do_ip_setsockopt(struct sock *sk, int level, (TCPF_LISTEN | TCPF_CLOSE)) && inet->inet_daddr != LOOPBACK4_IPV6)) { #endif - if (inet->opt) - icsk->icsk_ext_hdr_len -= inet->opt->optlen; + if (old) + icsk->icsk_ext_hdr_len -= old->opt.optlen; if (opt) - icsk->icsk_ext_hdr_len += opt->optlen; + icsk->icsk_ext_hdr_len += opt->opt.optlen; icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie); #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) } #endif } - opt = xchg(&inet->opt, opt); - kfree(opt); + rcu_assign_pointer(inet->inet_opt, opt); + if (old) + call_rcu(&old->rcu, opt_kfree_rcu); break; } case IP_PKTINFO: @@ -1081,12 +1090,16 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname, case IP_OPTIONS: { unsigned char optbuf[sizeof(struct ip_options)+40]; - struct ip_options * opt = (struct ip_options *)optbuf; + struct ip_options *opt = (struct ip_options *)optbuf; + struct ip_options_rcu *inet_opt; + + inet_opt = rcu_dereference_protected(inet->inet_opt, + sock_owned_by_user(sk)); opt->optlen = 0; - if (inet->opt) - memcpy(optbuf, inet->opt, - sizeof(struct ip_options)+ - inet->opt->optlen); + if (inet_opt) + memcpy(optbuf, &inet_opt->opt, + sizeof(struct ip_options) + + inet_opt->opt.optlen); release_sock(sk); if (opt->optlen == 0) diff --git a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c index 629067571f0..c857f6f49b0 100644 --- a/net/ipv4/ipcomp.c +++ b/net/ipv4/ipcomp.c @@ -27,7 +27,7 @@ static void ipcomp4_err(struct sk_buff *skb, u32 info) { struct net *net = dev_net(skb->dev); __be32 spi; - struct iphdr *iph = (struct iphdr *)skb->data; + const struct iphdr *iph = (const struct iphdr *)skb->data; struct ip_comp_hdr *ipch = (struct ip_comp_hdr *)(skb->data+(iph->ihl<<2)); struct xfrm_state *x; @@ -36,7 +36,7 @@ static void ipcomp4_err(struct sk_buff *skb, u32 info) return; spi = htonl(ntohs(ipch->cpi)); - x = xfrm_state_lookup(net, skb->mark, (xfrm_address_t *)&iph->daddr, + x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr, spi, IPPROTO_COMP, AF_INET); if (!x) return; diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c index cbff2ecccf3..ab7e5542c1c 100644 --- a/net/ipv4/ipconfig.c +++ b/net/ipv4/ipconfig.c @@ -87,8 +87,8 @@ #endif /* Define the friendly delay before and after opening net devices */ -#define CONF_PRE_OPEN 500 /* Before opening: 1/2 second */ -#define CONF_POST_OPEN 1 /* After opening: 1 second */ +#define CONF_POST_OPEN 10 /* After opening: 10 msecs */ +#define CONF_CARRIER_TIMEOUT 120000 /* Wait for carrier timeout */ /* Define the timeout for waiting for a DHCP/BOOTP/RARP reply */ #define CONF_OPEN_RETRIES 2 /* (Re)open devices twice */ @@ -188,14 +188,14 @@ struct ic_device { static struct ic_device *ic_first_dev __initdata = NULL;/* List of open device */ static struct net_device *ic_dev __initdata = NULL; /* Selected device */ -static bool __init ic_device_match(struct net_device *dev) +static bool __init ic_is_init_dev(struct net_device *dev) { - if (user_dev_name[0] ? !strcmp(dev->name, user_dev_name) : + if (dev->flags & IFF_LOOPBACK) + return false; + return user_dev_name[0] ? !strcmp(dev->name, user_dev_name) : (!(dev->flags & IFF_LOOPBACK) && (dev->flags & (IFF_POINTOPOINT|IFF_BROADCAST)) && - strncmp(dev->name, "dummy", 5))) - return true; - return false; + strncmp(dev->name, "dummy", 5)); } static int __init ic_open_devs(void) @@ -203,6 +203,7 @@ static int __init ic_open_devs(void) struct ic_device *d, **last; struct net_device *dev; unsigned short oflags; + unsigned long start; last = &ic_first_dev; rtnl_lock(); @@ -216,9 +217,7 @@ static int __init ic_open_devs(void) } for_each_netdev(&init_net, dev) { - if (dev->flags & IFF_LOOPBACK) - continue; - if (ic_device_match(dev)) { + if (ic_is_init_dev(dev)) { int able = 0; if (dev->mtu >= 364) able |= IC_BOOTP; @@ -252,6 +251,17 @@ static int __init ic_open_devs(void) dev->name, able, d->xid)); } } + + /* wait for a carrier on at least one device */ + start = jiffies; + while (jiffies - start < msecs_to_jiffies(CONF_CARRIER_TIMEOUT)) { + for_each_netdev(&init_net, dev) + if (ic_is_init_dev(dev) && netif_carrier_ok(dev)) + goto have_carrier; + + msleep(1); + } +have_carrier: rtnl_unlock(); *last = NULL; @@ -1324,14 +1334,13 @@ static int __init wait_for_devices(void) { int i; - msleep(CONF_PRE_OPEN); for (i = 0; i < DEVICE_WAIT_MAX; i++) { struct net_device *dev; int found = 0; rtnl_lock(); for_each_netdev(&init_net, dev) { - if (ic_device_match(dev)) { + if (ic_is_init_dev(dev)) { found = 1; break; } @@ -1378,7 +1387,7 @@ static int __init ip_auto_config(void) return err; /* Give drivers a chance to settle */ - ssleep(CONF_POST_OPEN); + msleep(CONF_POST_OPEN); /* * If the config information is insufficient (e.g., our IP address or diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index bfc17c5914e..378b20b7ca6 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -276,11 +276,6 @@ static struct ip_tunnel * ipip_tunnel_locate(struct net *net, dev_net_set(dev, net); - if (strchr(name, '%')) { - if (dev_alloc_name(dev, name) < 0) - goto failed_free; - } - nt = netdev_priv(dev); nt->parms = *parms; @@ -319,7 +314,7 @@ static int ipip_err(struct sk_buff *skb, u32 info) 8 bytes of packet payload. It means, that precise relaying of ICMP in the real Internet is absolutely infeasible. */ - struct iphdr *iph = (struct iphdr *)skb->data; + const struct iphdr *iph = (const struct iphdr *)skb->data; const int type = icmp_hdr(skb)->type; const int code = icmp_hdr(skb)->code; struct ip_tunnel *t; @@ -433,15 +428,16 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) { struct ip_tunnel *tunnel = netdev_priv(dev); struct pcpu_tstats *tstats; - struct iphdr *tiph = &tunnel->parms.iph; + const struct iphdr *tiph = &tunnel->parms.iph; u8 tos = tunnel->parms.iph.tos; __be16 df = tiph->frag_off; struct rtable *rt; /* Route to the other host */ struct net_device *tdev; /* Device to other host */ - struct iphdr *old_iph = ip_hdr(skb); + const struct iphdr *old_iph = ip_hdr(skb); struct iphdr *iph; /* Our new IP header */ unsigned int max_headroom; /* The extra header space needed */ __be32 dst = tiph->daddr; + struct flowi4 fl4; int mtu; if (skb->protocol != htons(ETH_P_IP)) @@ -460,7 +456,7 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) goto tx_error_icmp; } - rt = ip_route_output_ports(dev_net(dev), NULL, + rt = ip_route_output_ports(dev_net(dev), &fl4, NULL, dst, tiph->saddr, 0, 0, IPPROTO_IPIP, RT_TOS(tos), @@ -549,8 +545,8 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) iph->frag_off = df; iph->protocol = IPPROTO_IPIP; iph->tos = INET_ECN_encapsulate(tos, old_iph->tos); - iph->daddr = rt->rt_dst; - iph->saddr = rt->rt_src; + iph->daddr = fl4.daddr; + iph->saddr = fl4.saddr; if ((iph->ttl = tiph->ttl) == 0) iph->ttl = old_iph->ttl; @@ -572,19 +568,21 @@ static void ipip_tunnel_bind_dev(struct net_device *dev) { struct net_device *tdev = NULL; struct ip_tunnel *tunnel; - struct iphdr *iph; + const struct iphdr *iph; tunnel = netdev_priv(dev); iph = &tunnel->parms.iph; if (iph->daddr) { - struct rtable *rt = ip_route_output_ports(dev_net(dev), NULL, - iph->daddr, iph->saddr, - 0, 0, - IPPROTO_IPIP, - RT_TOS(iph->tos), - tunnel->parms.link); - + struct rtable *rt; + struct flowi4 fl4; + + rt = ip_route_output_ports(dev_net(dev), &fl4, NULL, + iph->daddr, iph->saddr, + 0, 0, + IPPROTO_IPIP, + RT_TOS(iph->tos), + tunnel->parms.link); if (!IS_ERR(rt)) { tdev = rt->dst.dev; ip_rt_put(rt); diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 1f62eaeb6de..aae2bd8cd92 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -1549,7 +1549,7 @@ static struct notifier_block ip_mr_notifier = { static void ip_encap(struct sk_buff *skb, __be32 saddr, __be32 daddr) { struct iphdr *iph; - struct iphdr *old_iph = ip_hdr(skb); + const struct iphdr *old_iph = ip_hdr(skb); skb_push(skb, sizeof(struct iphdr)); skb->transport_header = skb->network_header; @@ -1595,6 +1595,7 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt, struct vif_device *vif = &mrt->vif_table[vifi]; struct net_device *dev; struct rtable *rt; + struct flowi4 fl4; int encap = 0; if (vif->dev == NULL) @@ -1612,7 +1613,7 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt, #endif if (vif->flags & VIFF_TUNNEL) { - rt = ip_route_output_ports(net, NULL, + rt = ip_route_output_ports(net, &fl4, NULL, vif->remote, vif->local, 0, 0, IPPROTO_IPIP, @@ -1621,7 +1622,7 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt, goto out_free; encap = sizeof(struct iphdr); } else { - rt = ip_route_output_ports(net, NULL, iph->daddr, 0, + rt = ip_route_output_ports(net, &fl4, NULL, iph->daddr, 0, 0, 0, IPPROTO_IPIP, RT_TOS(iph->tos), vif->link); @@ -1788,12 +1789,14 @@ dont_forward: return 0; } -static struct mr_table *ipmr_rt_fib_lookup(struct net *net, struct rtable *rt) +static struct mr_table *ipmr_rt_fib_lookup(struct net *net, struct sk_buff *skb) { + struct rtable *rt = skb_rtable(skb); + struct iphdr *iph = ip_hdr(skb); struct flowi4 fl4 = { - .daddr = rt->rt_key_dst, - .saddr = rt->rt_key_src, - .flowi4_tos = rt->rt_tos, + .daddr = iph->daddr, + .saddr = iph->saddr, + .flowi4_tos = iph->tos, .flowi4_oif = rt->rt_oif, .flowi4_iif = rt->rt_iif, .flowi4_mark = rt->rt_mark, @@ -1825,7 +1828,7 @@ int ip_mr_input(struct sk_buff *skb) if (IPCB(skb)->flags & IPSKB_FORWARDED) goto dont_forward; - mrt = ipmr_rt_fib_lookup(net, skb_rtable(skb)); + mrt = ipmr_rt_fib_lookup(net, skb); if (IS_ERR(mrt)) { kfree_skb(skb); return PTR_ERR(mrt); @@ -1957,7 +1960,7 @@ int pim_rcv_v1(struct sk_buff *skb) pim = igmp_hdr(skb); - mrt = ipmr_rt_fib_lookup(net, skb_rtable(skb)); + mrt = ipmr_rt_fib_lookup(net, skb); if (IS_ERR(mrt)) goto drop; if (!mrt->mroute_do_pim || @@ -1989,7 +1992,7 @@ static int pim_rcv(struct sk_buff *skb) csum_fold(skb_checksum(skb, 0, skb->len, 0)))) goto drop; - mrt = ipmr_rt_fib_lookup(net, skb_rtable(skb)); + mrt = ipmr_rt_fib_lookup(net, skb); if (IS_ERR(mrt)) goto drop; if (__pim_rcv(mrt, skb, sizeof(*pim))) { @@ -2038,20 +2041,20 @@ rtattr_failure: return -EMSGSIZE; } -int ipmr_get_route(struct net *net, - struct sk_buff *skb, struct rtmsg *rtm, int nowait) +int ipmr_get_route(struct net *net, struct sk_buff *skb, + __be32 saddr, __be32 daddr, + struct rtmsg *rtm, int nowait) { - int err; - struct mr_table *mrt; struct mfc_cache *cache; - struct rtable *rt = skb_rtable(skb); + struct mr_table *mrt; + int err; mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); if (mrt == NULL) return -ENOENT; rcu_read_lock(); - cache = ipmr_cache_find(mrt, rt->rt_src, rt->rt_dst); + cache = ipmr_cache_find(mrt, saddr, daddr); if (cache == NULL) { struct sk_buff *skb2; @@ -2084,8 +2087,8 @@ int ipmr_get_route(struct net *net, skb_reset_network_header(skb2); iph = ip_hdr(skb2); iph->ihl = sizeof(struct iphdr) >> 2; - iph->saddr = rt->rt_src; - iph->daddr = rt->rt_dst; + iph->saddr = saddr; + iph->daddr = daddr; iph->version = 0; err = ipmr_cache_unresolved(mrt, vif, skb2); read_unlock(&mrt_lock); @@ -2541,7 +2544,8 @@ int __init ip_mr_init(void) goto add_proto_fail; } #endif - rtnl_register(RTNL_FAMILY_IPMR, RTM_GETROUTE, NULL, ipmr_rtm_dumproute); + rtnl_register(RTNL_FAMILY_IPMR, RTM_GETROUTE, + NULL, ipmr_rtm_dumproute, NULL); return 0; #ifdef CONFIG_IP_PIMSM_V2 diff --git a/net/ipv4/netfilter/nf_nat_helper.c b/net/ipv4/netfilter/nf_nat_helper.c index 31427fb57aa..99cfa28b6d3 100644 --- a/net/ipv4/netfilter/nf_nat_helper.c +++ b/net/ipv4/netfilter/nf_nat_helper.c @@ -153,7 +153,7 @@ void nf_nat_set_seq_adjust(struct nf_conn *ct, enum ip_conntrack_info ctinfo, } EXPORT_SYMBOL_GPL(nf_nat_set_seq_adjust); -static void nf_nat_csum(struct sk_buff *skb, struct iphdr *iph, void *data, +static void nf_nat_csum(struct sk_buff *skb, const struct iphdr *iph, void *data, int datalen, __sum16 *check, int oldlen) { struct rtable *rt = skb_rtable(skb); diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c new file mode 100644 index 00000000000..9aaa67165f4 --- /dev/null +++ b/net/ipv4/ping.c @@ -0,0 +1,932 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * "Ping" sockets + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Based on ipv4/udp.c code. + * + * Authors: Vasiliy Kulikov / Openwall (for Linux 2.6), + * Pavel Kankovsky (for Linux 2.4.32) + * + * Pavel gave all rights to bugs to Vasiliy, + * none of the bugs are Pavel's now. + * + */ + +#include <asm/system.h> +#include <linux/uaccess.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/socket.h> +#include <linux/sockios.h> +#include <linux/in.h> +#include <linux/errno.h> +#include <linux/timer.h> +#include <linux/mm.h> +#include <linux/inet.h> +#include <linux/netdevice.h> +#include <net/snmp.h> +#include <net/ip.h> +#include <net/ipv6.h> +#include <net/icmp.h> +#include <net/protocol.h> +#include <linux/skbuff.h> +#include <linux/proc_fs.h> +#include <net/sock.h> +#include <net/ping.h> +#include <net/icmp.h> +#include <net/udp.h> +#include <net/route.h> +#include <net/inet_common.h> +#include <net/checksum.h> + + +static struct ping_table ping_table; + +static u16 ping_port_rover; + +static inline int ping_hashfn(struct net *net, unsigned num, unsigned mask) +{ + int res = (num + net_hash_mix(net)) & mask; + pr_debug("hash(%d) = %d\n", num, res); + return res; +} + +static inline struct hlist_nulls_head *ping_hashslot(struct ping_table *table, + struct net *net, unsigned num) +{ + return &table->hash[ping_hashfn(net, num, PING_HTABLE_MASK)]; +} + +static int ping_v4_get_port(struct sock *sk, unsigned short ident) +{ + struct hlist_nulls_node *node; + struct hlist_nulls_head *hlist; + struct inet_sock *isk, *isk2; + struct sock *sk2 = NULL; + + isk = inet_sk(sk); + write_lock_bh(&ping_table.lock); + if (ident == 0) { + u32 i; + u16 result = ping_port_rover + 1; + + for (i = 0; i < (1L << 16); i++, result++) { + if (!result) + result++; /* avoid zero */ + hlist = ping_hashslot(&ping_table, sock_net(sk), + result); + ping_portaddr_for_each_entry(sk2, node, hlist) { + isk2 = inet_sk(sk2); + + if (isk2->inet_num == result) + goto next_port; + } + + /* found */ + ping_port_rover = ident = result; + break; +next_port: + ; + } + if (i >= (1L << 16)) + goto fail; + } else { + hlist = ping_hashslot(&ping_table, sock_net(sk), ident); + ping_portaddr_for_each_entry(sk2, node, hlist) { + isk2 = inet_sk(sk2); + + if ((isk2->inet_num == ident) && + (sk2 != sk) && + (!sk2->sk_reuse || !sk->sk_reuse)) + goto fail; + } + } + + pr_debug("found port/ident = %d\n", ident); + isk->inet_num = ident; + if (sk_unhashed(sk)) { + pr_debug("was not hashed\n"); + sock_hold(sk); + hlist_nulls_add_head(&sk->sk_nulls_node, hlist); + sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); + } + write_unlock_bh(&ping_table.lock); + return 0; + +fail: + write_unlock_bh(&ping_table.lock); + return 1; +} + +static void ping_v4_hash(struct sock *sk) +{ + pr_debug("ping_v4_hash(sk->port=%u)\n", inet_sk(sk)->inet_num); + BUG(); /* "Please do not press this button again." */ +} + +static void ping_v4_unhash(struct sock *sk) +{ + struct inet_sock *isk = inet_sk(sk); + pr_debug("ping_v4_unhash(isk=%p,isk->num=%u)\n", isk, isk->inet_num); + if (sk_hashed(sk)) { + write_lock_bh(&ping_table.lock); + hlist_nulls_del(&sk->sk_nulls_node); + sock_put(sk); + isk->inet_num = isk->inet_sport = 0; + sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); + write_unlock_bh(&ping_table.lock); + } +} + +static struct sock *ping_v4_lookup(struct net *net, u32 saddr, u32 daddr, + u16 ident, int dif) +{ + struct hlist_nulls_head *hslot = ping_hashslot(&ping_table, net, ident); + struct sock *sk = NULL; + struct inet_sock *isk; + struct hlist_nulls_node *hnode; + + pr_debug("try to find: num = %d, daddr = %ld, dif = %d\n", + (int)ident, (unsigned long)daddr, dif); + read_lock_bh(&ping_table.lock); + + ping_portaddr_for_each_entry(sk, hnode, hslot) { + isk = inet_sk(sk); + + pr_debug("found: %p: num = %d, daddr = %ld, dif = %d\n", sk, + (int)isk->inet_num, (unsigned long)isk->inet_rcv_saddr, + sk->sk_bound_dev_if); + + pr_debug("iterate\n"); + if (isk->inet_num != ident) + continue; + if (isk->inet_rcv_saddr && isk->inet_rcv_saddr != daddr) + continue; + if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif) + continue; + + sock_hold(sk); + goto exit; + } + + sk = NULL; +exit: + read_unlock_bh(&ping_table.lock); + + return sk; +} + +static void inet_get_ping_group_range_net(struct net *net, gid_t *low, + gid_t *high) +{ + gid_t *data = net->ipv4.sysctl_ping_group_range; + unsigned seq; + do { + seq = read_seqbegin(&sysctl_local_ports.lock); + + *low = data[0]; + *high = data[1]; + } while (read_seqretry(&sysctl_local_ports.lock, seq)); +} + + +static int ping_init_sock(struct sock *sk) +{ + struct net *net = sock_net(sk); + gid_t group = current_egid(); + gid_t range[2]; + struct group_info *group_info = get_current_groups(); + int i, j, count = group_info->ngroups; + + inet_get_ping_group_range_net(net, range, range+1); + if (range[0] <= group && group <= range[1]) + return 0; + + for (i = 0; i < group_info->nblocks; i++) { + int cp_count = min_t(int, NGROUPS_PER_BLOCK, count); + + for (j = 0; j < cp_count; j++) { + group = group_info->blocks[i][j]; + if (range[0] <= group && group <= range[1]) + return 0; + } + + count -= cp_count; + } + + return -EACCES; +} + +static void ping_close(struct sock *sk, long timeout) +{ + pr_debug("ping_close(sk=%p,sk->num=%u)\n", + inet_sk(sk), inet_sk(sk)->inet_num); + pr_debug("isk->refcnt = %d\n", sk->sk_refcnt.counter); + + sk_common_release(sk); +} + +/* + * We need our own bind because there are no privileged id's == local ports. + * Moreover, we don't allow binding to multi- and broadcast addresses. + */ + +static int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) +{ + struct sockaddr_in *addr = (struct sockaddr_in *)uaddr; + struct inet_sock *isk = inet_sk(sk); + unsigned short snum; + int chk_addr_ret; + int err; + + if (addr_len < sizeof(struct sockaddr_in)) + return -EINVAL; + + pr_debug("ping_v4_bind(sk=%p,sa_addr=%08x,sa_port=%d)\n", + sk, addr->sin_addr.s_addr, ntohs(addr->sin_port)); + + chk_addr_ret = inet_addr_type(sock_net(sk), addr->sin_addr.s_addr); + if (addr->sin_addr.s_addr == INADDR_ANY) + chk_addr_ret = RTN_LOCAL; + + if ((sysctl_ip_nonlocal_bind == 0 && + isk->freebind == 0 && isk->transparent == 0 && + chk_addr_ret != RTN_LOCAL) || + chk_addr_ret == RTN_MULTICAST || + chk_addr_ret == RTN_BROADCAST) + return -EADDRNOTAVAIL; + + lock_sock(sk); + + err = -EINVAL; + if (isk->inet_num != 0) + goto out; + + err = -EADDRINUSE; + isk->inet_rcv_saddr = isk->inet_saddr = addr->sin_addr.s_addr; + snum = ntohs(addr->sin_port); + if (ping_v4_get_port(sk, snum) != 0) { + isk->inet_saddr = isk->inet_rcv_saddr = 0; + goto out; + } + + pr_debug("after bind(): num = %d, daddr = %ld, dif = %d\n", + (int)isk->inet_num, + (unsigned long) isk->inet_rcv_saddr, + (int)sk->sk_bound_dev_if); + + err = 0; + if (isk->inet_rcv_saddr) + sk->sk_userlocks |= SOCK_BINDADDR_LOCK; + if (snum) + sk->sk_userlocks |= SOCK_BINDPORT_LOCK; + isk->inet_sport = htons(isk->inet_num); + isk->inet_daddr = 0; + isk->inet_dport = 0; + sk_dst_reset(sk); +out: + release_sock(sk); + pr_debug("ping_v4_bind -> %d\n", err); + return err; +} + +/* + * Is this a supported type of ICMP message? + */ + +static inline int ping_supported(int type, int code) +{ + if (type == ICMP_ECHO && code == 0) + return 1; + return 0; +} + +/* + * This routine is called by the ICMP module when it gets some + * sort of error condition. + */ + +static int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb); + +void ping_err(struct sk_buff *skb, u32 info) +{ + struct iphdr *iph = (struct iphdr *)skb->data; + struct icmphdr *icmph = (struct icmphdr *)(skb->data+(iph->ihl<<2)); + struct inet_sock *inet_sock; + int type = icmph->type; + int code = icmph->code; + struct net *net = dev_net(skb->dev); + struct sock *sk; + int harderr; + int err; + + /* We assume the packet has already been checked by icmp_unreach */ + + if (!ping_supported(icmph->type, icmph->code)) + return; + + pr_debug("ping_err(type=%04x,code=%04x,id=%04x,seq=%04x)\n", type, + code, ntohs(icmph->un.echo.id), ntohs(icmph->un.echo.sequence)); + + sk = ping_v4_lookup(net, iph->daddr, iph->saddr, + ntohs(icmph->un.echo.id), skb->dev->ifindex); + if (sk == NULL) { + ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS); + pr_debug("no socket, dropping\n"); + return; /* No socket for error */ + } + pr_debug("err on socket %p\n", sk); + + err = 0; + harderr = 0; + inet_sock = inet_sk(sk); + + switch (type) { + default: + case ICMP_TIME_EXCEEDED: + err = EHOSTUNREACH; + break; + case ICMP_SOURCE_QUENCH: + /* This is not a real error but ping wants to see it. + * Report it with some fake errno. */ + err = EREMOTEIO; + break; + case ICMP_PARAMETERPROB: + err = EPROTO; + harderr = 1; + break; + case ICMP_DEST_UNREACH: + if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */ + if (inet_sock->pmtudisc != IP_PMTUDISC_DONT) { + err = EMSGSIZE; + harderr = 1; + break; + } + goto out; + } + err = EHOSTUNREACH; + if (code <= NR_ICMP_UNREACH) { + harderr = icmp_err_convert[code].fatal; + err = icmp_err_convert[code].errno; + } + break; + case ICMP_REDIRECT: + /* See ICMP_SOURCE_QUENCH */ + err = EREMOTEIO; + break; + } + + /* + * RFC1122: OK. Passes ICMP errors back to application, as per + * 4.1.3.3. + */ + if (!inet_sock->recverr) { + if (!harderr || sk->sk_state != TCP_ESTABLISHED) + goto out; + } else { + ip_icmp_error(sk, skb, err, 0 /* no remote port */, + info, (u8 *)icmph); + } + sk->sk_err = err; + sk->sk_error_report(sk); +out: + sock_put(sk); +} + +/* + * Copy and checksum an ICMP Echo packet from user space into a buffer. + */ + +struct pingfakehdr { + struct icmphdr icmph; + struct iovec *iov; + u32 wcheck; +}; + +static int ping_getfrag(void *from, char * to, + int offset, int fraglen, int odd, struct sk_buff *skb) +{ + struct pingfakehdr *pfh = (struct pingfakehdr *)from; + + if (offset == 0) { + if (fraglen < sizeof(struct icmphdr)) + BUG(); + if (csum_partial_copy_fromiovecend(to + sizeof(struct icmphdr), + pfh->iov, 0, fraglen - sizeof(struct icmphdr), + &pfh->wcheck)) + return -EFAULT; + + return 0; + } + if (offset < sizeof(struct icmphdr)) + BUG(); + if (csum_partial_copy_fromiovecend + (to, pfh->iov, offset - sizeof(struct icmphdr), + fraglen, &pfh->wcheck)) + return -EFAULT; + return 0; +} + +static int ping_push_pending_frames(struct sock *sk, struct pingfakehdr *pfh, + struct flowi4 *fl4) +{ + struct sk_buff *skb = skb_peek(&sk->sk_write_queue); + + pfh->wcheck = csum_partial((char *)&pfh->icmph, + sizeof(struct icmphdr), pfh->wcheck); + pfh->icmph.checksum = csum_fold(pfh->wcheck); + memcpy(icmp_hdr(skb), &pfh->icmph, sizeof(struct icmphdr)); + skb->ip_summed = CHECKSUM_NONE; + return ip_push_pending_frames(sk, fl4); +} + +static int ping_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, + size_t len) +{ + struct net *net = sock_net(sk); + struct flowi4 fl4; + struct inet_sock *inet = inet_sk(sk); + struct ipcm_cookie ipc; + struct icmphdr user_icmph; + struct pingfakehdr pfh; + struct rtable *rt = NULL; + struct ip_options_data opt_copy; + int free = 0; + u32 saddr, daddr, faddr; + u8 tos; + int err; + + pr_debug("ping_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num); + + + if (len > 0xFFFF) + return -EMSGSIZE; + + /* + * Check the flags. + */ + + /* Mirror BSD error message compatibility */ + if (msg->msg_flags & MSG_OOB) + return -EOPNOTSUPP; + + /* + * Fetch the ICMP header provided by the userland. + * iovec is modified! + */ + + if (memcpy_fromiovec((u8 *)&user_icmph, msg->msg_iov, + sizeof(struct icmphdr))) + return -EFAULT; + if (!ping_supported(user_icmph.type, user_icmph.code)) + return -EINVAL; + + /* + * Get and verify the address. + */ + + if (msg->msg_name) { + struct sockaddr_in *usin = (struct sockaddr_in *)msg->msg_name; + if (msg->msg_namelen < sizeof(*usin)) + return -EINVAL; + if (usin->sin_family != AF_INET) + return -EINVAL; + daddr = usin->sin_addr.s_addr; + /* no remote port */ + } else { + if (sk->sk_state != TCP_ESTABLISHED) + return -EDESTADDRREQ; + daddr = inet->inet_daddr; + /* no remote port */ + } + + ipc.addr = inet->inet_saddr; + ipc.opt = NULL; + ipc.oif = sk->sk_bound_dev_if; + ipc.tx_flags = 0; + err = sock_tx_timestamp(sk, &ipc.tx_flags); + if (err) + return err; + + if (msg->msg_controllen) { + err = ip_cmsg_send(sock_net(sk), msg, &ipc); + if (err) + return err; + if (ipc.opt) + free = 1; + } + if (!ipc.opt) { + struct ip_options_rcu *inet_opt; + + rcu_read_lock(); + inet_opt = rcu_dereference(inet->inet_opt); + if (inet_opt) { + memcpy(&opt_copy, inet_opt, + sizeof(*inet_opt) + inet_opt->opt.optlen); + ipc.opt = &opt_copy.opt; + } + rcu_read_unlock(); + } + + saddr = ipc.addr; + ipc.addr = faddr = daddr; + + if (ipc.opt && ipc.opt->opt.srr) { + if (!daddr) + return -EINVAL; + faddr = ipc.opt->opt.faddr; + } + tos = RT_TOS(inet->tos); + if (sock_flag(sk, SOCK_LOCALROUTE) || + (msg->msg_flags & MSG_DONTROUTE) || + (ipc.opt && ipc.opt->opt.is_strictroute)) { + tos |= RTO_ONLINK; + } + + if (ipv4_is_multicast(daddr)) { + if (!ipc.oif) + ipc.oif = inet->mc_index; + if (!saddr) + saddr = inet->mc_addr; + } + + flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos, + RT_SCOPE_UNIVERSE, sk->sk_protocol, + inet_sk_flowi_flags(sk), faddr, saddr, 0, 0); + + security_sk_classify_flow(sk, flowi4_to_flowi(&fl4)); + rt = ip_route_output_flow(net, &fl4, sk); + if (IS_ERR(rt)) { + err = PTR_ERR(rt); + rt = NULL; + if (err == -ENETUNREACH) + IP_INC_STATS_BH(net, IPSTATS_MIB_OUTNOROUTES); + goto out; + } + + err = -EACCES; + if ((rt->rt_flags & RTCF_BROADCAST) && + !sock_flag(sk, SOCK_BROADCAST)) + goto out; + + if (msg->msg_flags & MSG_CONFIRM) + goto do_confirm; +back_from_confirm: + + if (!ipc.addr) + ipc.addr = fl4.daddr; + + lock_sock(sk); + + pfh.icmph.type = user_icmph.type; /* already checked */ + pfh.icmph.code = user_icmph.code; /* ditto */ + pfh.icmph.checksum = 0; + pfh.icmph.un.echo.id = inet->inet_sport; + pfh.icmph.un.echo.sequence = user_icmph.un.echo.sequence; + pfh.iov = msg->msg_iov; + pfh.wcheck = 0; + + err = ip_append_data(sk, &fl4, ping_getfrag, &pfh, len, + 0, &ipc, &rt, msg->msg_flags); + if (err) + ip_flush_pending_frames(sk); + else + err = ping_push_pending_frames(sk, &pfh, &fl4); + release_sock(sk); + +out: + ip_rt_put(rt); + if (free) + kfree(ipc.opt); + if (!err) { + icmp_out_count(sock_net(sk), user_icmph.type); + return len; + } + return err; + +do_confirm: + dst_confirm(&rt->dst); + if (!(msg->msg_flags & MSG_PROBE) || len) + goto back_from_confirm; + err = 0; + goto out; +} + +static int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, + size_t len, int noblock, int flags, int *addr_len) +{ + struct inet_sock *isk = inet_sk(sk); + struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name; + struct sk_buff *skb; + int copied, err; + + pr_debug("ping_recvmsg(sk=%p,sk->num=%u)\n", isk, isk->inet_num); + + if (flags & MSG_OOB) + goto out; + + if (addr_len) + *addr_len = sizeof(*sin); + + if (flags & MSG_ERRQUEUE) + return ip_recv_error(sk, msg, len); + + skb = skb_recv_datagram(sk, flags, noblock, &err); + if (!skb) + goto out; + + copied = skb->len; + if (copied > len) { + msg->msg_flags |= MSG_TRUNC; + copied = len; + } + + /* Don't bother checking the checksum */ + err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + if (err) + goto done; + + sock_recv_timestamp(msg, sk, skb); + + /* Copy the address. */ + if (sin) { + sin->sin_family = AF_INET; + sin->sin_port = 0 /* skb->h.uh->source */; + sin->sin_addr.s_addr = ip_hdr(skb)->saddr; + memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); + } + if (isk->cmsg_flags) + ip_cmsg_recv(msg, skb); + err = copied; + +done: + skb_free_datagram(sk, skb); +out: + pr_debug("ping_recvmsg -> %d\n", err); + return err; +} + +static int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) +{ + pr_debug("ping_queue_rcv_skb(sk=%p,sk->num=%d,skb=%p)\n", + inet_sk(sk), inet_sk(sk)->inet_num, skb); + if (sock_queue_rcv_skb(sk, skb) < 0) { + ICMP_INC_STATS_BH(sock_net(sk), ICMP_MIB_INERRORS); + kfree_skb(skb); + pr_debug("ping_queue_rcv_skb -> failed\n"); + return -1; + } + return 0; +} + + +/* + * All we need to do is get the socket. + */ + +void ping_rcv(struct sk_buff *skb) +{ + struct sock *sk; + struct net *net = dev_net(skb->dev); + struct iphdr *iph = ip_hdr(skb); + struct icmphdr *icmph = icmp_hdr(skb); + u32 saddr = iph->saddr; + u32 daddr = iph->daddr; + + /* We assume the packet has already been checked by icmp_rcv */ + + pr_debug("ping_rcv(skb=%p,id=%04x,seq=%04x)\n", + skb, ntohs(icmph->un.echo.id), ntohs(icmph->un.echo.sequence)); + + /* Push ICMP header back */ + skb_push(skb, skb->data - (u8 *)icmph); + + sk = ping_v4_lookup(net, saddr, daddr, ntohs(icmph->un.echo.id), + skb->dev->ifindex); + if (sk != NULL) { + pr_debug("rcv on socket %p\n", sk); + ping_queue_rcv_skb(sk, skb_get(skb)); + sock_put(sk); + return; + } + pr_debug("no socket, dropping\n"); + + /* We're called from icmp_rcv(). kfree_skb() is done there. */ +} + +struct proto ping_prot = { + .name = "PING", + .owner = THIS_MODULE, + .init = ping_init_sock, + .close = ping_close, + .connect = ip4_datagram_connect, + .disconnect = udp_disconnect, + .setsockopt = ip_setsockopt, + .getsockopt = ip_getsockopt, + .sendmsg = ping_sendmsg, + .recvmsg = ping_recvmsg, + .bind = ping_bind, + .backlog_rcv = ping_queue_rcv_skb, + .hash = ping_v4_hash, + .unhash = ping_v4_unhash, + .get_port = ping_v4_get_port, + .obj_size = sizeof(struct inet_sock), +}; +EXPORT_SYMBOL(ping_prot); + +#ifdef CONFIG_PROC_FS + +static struct sock *ping_get_first(struct seq_file *seq, int start) +{ + struct sock *sk; + struct ping_iter_state *state = seq->private; + struct net *net = seq_file_net(seq); + + for (state->bucket = start; state->bucket < PING_HTABLE_SIZE; + ++state->bucket) { + struct hlist_nulls_node *node; + struct hlist_nulls_head *hslot; + + hslot = &ping_table.hash[state->bucket]; + + if (hlist_nulls_empty(hslot)) + continue; + + sk_nulls_for_each(sk, node, hslot) { + if (net_eq(sock_net(sk), net)) + goto found; + } + } + sk = NULL; +found: + return sk; +} + +static struct sock *ping_get_next(struct seq_file *seq, struct sock *sk) +{ + struct ping_iter_state *state = seq->private; + struct net *net = seq_file_net(seq); + + do { + sk = sk_nulls_next(sk); + } while (sk && (!net_eq(sock_net(sk), net))); + + if (!sk) + return ping_get_first(seq, state->bucket + 1); + return sk; +} + +static struct sock *ping_get_idx(struct seq_file *seq, loff_t pos) +{ + struct sock *sk = ping_get_first(seq, 0); + + if (sk) + while (pos && (sk = ping_get_next(seq, sk)) != NULL) + --pos; + return pos ? NULL : sk; +} + +static void *ping_seq_start(struct seq_file *seq, loff_t *pos) +{ + struct ping_iter_state *state = seq->private; + state->bucket = 0; + + read_lock_bh(&ping_table.lock); + + return *pos ? ping_get_idx(seq, *pos-1) : SEQ_START_TOKEN; +} + +static void *ping_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct sock *sk; + + if (v == SEQ_START_TOKEN) + sk = ping_get_idx(seq, 0); + else + sk = ping_get_next(seq, v); + + ++*pos; + return sk; +} + +static void ping_seq_stop(struct seq_file *seq, void *v) +{ + read_unlock_bh(&ping_table.lock); +} + +static void ping_format_sock(struct sock *sp, struct seq_file *f, + int bucket, int *len) +{ + struct inet_sock *inet = inet_sk(sp); + __be32 dest = inet->inet_daddr; + __be32 src = inet->inet_rcv_saddr; + __u16 destp = ntohs(inet->inet_dport); + __u16 srcp = ntohs(inet->inet_sport); + + seq_printf(f, "%5d: %08X:%04X %08X:%04X" + " %02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %pK %d%n", + bucket, src, srcp, dest, destp, sp->sk_state, + sk_wmem_alloc_get(sp), + sk_rmem_alloc_get(sp), + 0, 0L, 0, sock_i_uid(sp), 0, sock_i_ino(sp), + atomic_read(&sp->sk_refcnt), sp, + atomic_read(&sp->sk_drops), len); +} + +static int ping_seq_show(struct seq_file *seq, void *v) +{ + if (v == SEQ_START_TOKEN) + seq_printf(seq, "%-127s\n", + " sl local_address rem_address st tx_queue " + "rx_queue tr tm->when retrnsmt uid timeout " + "inode ref pointer drops"); + else { + struct ping_iter_state *state = seq->private; + int len; + + ping_format_sock(v, seq, state->bucket, &len); + seq_printf(seq, "%*s\n", 127 - len, ""); + } + return 0; +} + +static const struct seq_operations ping_seq_ops = { + .show = ping_seq_show, + .start = ping_seq_start, + .next = ping_seq_next, + .stop = ping_seq_stop, +}; + +static int ping_seq_open(struct inode *inode, struct file *file) +{ + return seq_open_net(inode, file, &ping_seq_ops, + sizeof(struct ping_iter_state)); +} + +static const struct file_operations ping_seq_fops = { + .open = ping_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_net, +}; + +static int ping_proc_register(struct net *net) +{ + struct proc_dir_entry *p; + int rc = 0; + + p = proc_net_fops_create(net, "icmp", S_IRUGO, &ping_seq_fops); + if (!p) + rc = -ENOMEM; + return rc; +} + +static void ping_proc_unregister(struct net *net) +{ + proc_net_remove(net, "icmp"); +} + + +static int __net_init ping_proc_init_net(struct net *net) +{ + return ping_proc_register(net); +} + +static void __net_exit ping_proc_exit_net(struct net *net) +{ + ping_proc_unregister(net); +} + +static struct pernet_operations ping_net_ops = { + .init = ping_proc_init_net, + .exit = ping_proc_exit_net, +}; + +int __init ping_proc_init(void) +{ + return register_pernet_subsys(&ping_net_ops); +} + +void ping_proc_exit(void) +{ + unregister_pernet_subsys(&ping_net_ops); +} + +#endif + +void __init ping_init(void) +{ + int i; + + for (i = 0; i < PING_HTABLE_SIZE; i++) + INIT_HLIST_NULLS_HEAD(&ping_table.hash[i], i); + rwlock_init(&ping_table.lock); +} diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 2b50cc2da90..c9893d43242 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -154,7 +154,7 @@ static __inline__ int icmp_filter(struct sock *sk, struct sk_buff *skb) * RFC 1122: SHOULD pass TOS value up to the transport layer. * -> It does. And not only TOS, but all IP header. */ -static int raw_v4_input(struct sk_buff *skb, struct iphdr *iph, int hash) +static int raw_v4_input(struct sk_buff *skb, const struct iphdr *iph, int hash) { struct sock *sk; struct hlist_head *head; @@ -247,7 +247,7 @@ static void raw_err(struct sock *sk, struct sk_buff *skb, u32 info) } if (inet->recverr) { - struct iphdr *iph = (struct iphdr *)skb->data; + const struct iphdr *iph = (const struct iphdr *)skb->data; u8 *payload = skb->data + (iph->ihl << 2); if (inet->hdrincl) @@ -265,7 +265,7 @@ void raw_icmp_error(struct sk_buff *skb, int protocol, u32 info) { int hash; struct sock *raw_sk; - struct iphdr *iph; + const struct iphdr *iph; struct net *net; hash = protocol & (RAW_HTABLE_SIZE - 1); @@ -273,7 +273,7 @@ void raw_icmp_error(struct sk_buff *skb, int protocol, u32 info) read_lock(&raw_v4_hashinfo.lock); raw_sk = sk_head(&raw_v4_hashinfo.ht[hash]); if (raw_sk != NULL) { - iph = (struct iphdr *)skb->data; + iph = (const struct iphdr *)skb->data; net = dev_net(skb->dev); while ((raw_sk = __raw_v4_lookup(net, raw_sk, protocol, @@ -281,7 +281,7 @@ void raw_icmp_error(struct sk_buff *skb, int protocol, u32 info) skb->dev->ifindex)) != NULL) { raw_err(raw_sk, skb, info); raw_sk = sk_next(raw_sk); - iph = (struct iphdr *)skb->data; + iph = (const struct iphdr *)skb->data; } } read_unlock(&raw_v4_hashinfo.lock); @@ -314,9 +314,10 @@ int raw_rcv(struct sock *sk, struct sk_buff *skb) return 0; } -static int raw_send_hdrinc(struct sock *sk, void *from, size_t length, - struct rtable **rtp, - unsigned int flags) +static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4, + void *from, size_t length, + struct rtable **rtp, + unsigned int flags) { struct inet_sock *inet = inet_sk(sk); struct net *net = sock_net(sk); @@ -327,7 +328,7 @@ static int raw_send_hdrinc(struct sock *sk, void *from, size_t length, struct rtable *rt = *rtp; if (length > rt->dst.dev->mtu) { - ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->inet_dport, + ip_local_error(sk, EMSGSIZE, fl4->daddr, inet->inet_dport, rt->dst.dev->mtu); return -EMSGSIZE; } @@ -372,7 +373,7 @@ static int raw_send_hdrinc(struct sock *sk, void *from, size_t length, if (iphlen >= sizeof(*iph)) { if (!iph->saddr) - iph->saddr = rt->rt_src; + iph->saddr = fl4->saddr; iph->check = 0; iph->tot_len = htons(length); if (!iph->id) @@ -455,11 +456,13 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, struct inet_sock *inet = inet_sk(sk); struct ipcm_cookie ipc; struct rtable *rt = NULL; + struct flowi4 fl4; int free = 0; __be32 daddr; __be32 saddr; u8 tos; int err; + struct ip_options_data opt_copy; err = -EMSGSIZE; if (len > 0xFFFF) @@ -520,8 +523,18 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, saddr = ipc.addr; ipc.addr = daddr; - if (!ipc.opt) - ipc.opt = inet->opt; + if (!ipc.opt) { + struct ip_options_rcu *inet_opt; + + rcu_read_lock(); + inet_opt = rcu_dereference(inet->inet_opt); + if (inet_opt) { + memcpy(&opt_copy, inet_opt, + sizeof(*inet_opt) + inet_opt->opt.optlen); + ipc.opt = &opt_copy.opt; + } + rcu_read_unlock(); + } if (ipc.opt) { err = -EINVAL; @@ -530,10 +543,10 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, */ if (inet->hdrincl) goto done; - if (ipc.opt->srr) { + if (ipc.opt->opt.srr) { if (!daddr) goto done; - daddr = ipc.opt->faddr; + daddr = ipc.opt->opt.faddr; } } tos = RT_CONN_FLAGS(sk); @@ -547,27 +560,23 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, saddr = inet->mc_addr; } - { - struct flowi4 fl4; + flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos, + RT_SCOPE_UNIVERSE, + inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol, + FLOWI_FLAG_CAN_SLEEP, daddr, saddr, 0, 0); - flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos, - RT_SCOPE_UNIVERSE, - inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol, - FLOWI_FLAG_CAN_SLEEP, daddr, saddr, 0, 0); - - if (!inet->hdrincl) { - err = raw_probe_proto_opt(&fl4, msg); - if (err) - goto done; - } - - security_sk_classify_flow(sk, flowi4_to_flowi(&fl4)); - rt = ip_route_output_flow(sock_net(sk), &fl4, sk); - if (IS_ERR(rt)) { - err = PTR_ERR(rt); - rt = NULL; + if (!inet->hdrincl) { + err = raw_probe_proto_opt(&fl4, msg); + if (err) goto done; - } + } + + security_sk_classify_flow(sk, flowi4_to_flowi(&fl4)); + rt = ip_route_output_flow(sock_net(sk), &fl4, sk); + if (IS_ERR(rt)) { + err = PTR_ERR(rt); + rt = NULL; + goto done; } err = -EACCES; @@ -579,19 +588,20 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, back_from_confirm: if (inet->hdrincl) - err = raw_send_hdrinc(sk, msg->msg_iov, len, - &rt, msg->msg_flags); + err = raw_send_hdrinc(sk, &fl4, msg->msg_iov, len, + &rt, msg->msg_flags); else { if (!ipc.addr) - ipc.addr = rt->rt_dst; + ipc.addr = fl4.daddr; lock_sock(sk); - err = ip_append_data(sk, ip_generic_getfrag, msg->msg_iov, len, 0, - &ipc, &rt, msg->msg_flags); + err = ip_append_data(sk, &fl4, ip_generic_getfrag, + msg->msg_iov, len, 0, + &ipc, &rt, msg->msg_flags); if (err) ip_flush_pending_frames(sk); else if (!(msg->msg_flags & MSG_MORE)) { - err = ip_push_pending_frames(sk); + err = ip_push_pending_frames(sk, &fl4); if (err == -ENOBUFS && !inet->recverr) err = 0; } @@ -969,7 +979,7 @@ static void raw_sock_seq_show(struct seq_file *seq, struct sock *sp, int i) srcp = inet->inet_num; seq_printf(seq, "%4d: %08X:%04X %08X:%04X" - " %02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p %d\n", + " %02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %pK %d\n", i, src, srcp, dest, destp, sp->sk_state, sk_wmem_alloc_get(sp), sk_rmem_alloc_get(sp), diff --git a/net/ipv4/route.c b/net/ipv4/route.c index e9aee81de3e..aa29c629135 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -156,7 +156,7 @@ static u32 *ipv4_cow_metrics(struct dst_entry *dst, unsigned long old) u32 *p = NULL; if (!rt->peer) - rt_bind_peer(rt, 1); + rt_bind_peer(rt, rt->rt_dst, 1); peer = rt->peer; if (peer) { @@ -424,7 +424,7 @@ static int rt_cache_seq_show(struct seq_file *seq, void *v) dst_metric(&r->dst, RTAX_WINDOW), (int)((dst_metric(&r->dst, RTAX_RTT) >> 3) + dst_metric(&r->dst, RTAX_RTTVAR)), - r->rt_tos, + r->rt_key_tos, r->dst.hh ? atomic_read(&r->dst.hh->hh_refcnt) : -1, r->dst.hh ? (r->dst.hh->hh_output == dev_queue_xmit) : 0, @@ -724,7 +724,7 @@ static inline int compare_keys(struct rtable *rt1, struct rtable *rt2) return (((__force u32)rt1->rt_key_dst ^ (__force u32)rt2->rt_key_dst) | ((__force u32)rt1->rt_key_src ^ (__force u32)rt2->rt_key_src) | (rt1->rt_mark ^ rt2->rt_mark) | - (rt1->rt_tos ^ rt2->rt_tos) | + (rt1->rt_key_tos ^ rt2->rt_key_tos) | (rt1->rt_oif ^ rt2->rt_oif) | (rt1->rt_iif ^ rt2->rt_iif)) == 0; } @@ -968,10 +968,6 @@ static int rt_garbage_collect(struct dst_ops *ops) break; expire >>= 1; -#if RT_CACHE_DEBUG >= 2 - printk(KERN_DEBUG "expire>> %u %d %d %d\n", expire, - dst_entries_get_fast(&ipv4_dst_ops), goal, i); -#endif if (dst_entries_get_fast(&ipv4_dst_ops) < ip_rt_max_size) goto out; @@ -992,10 +988,6 @@ work_done: dst_entries_get_fast(&ipv4_dst_ops) < ipv4_dst_ops.gc_thresh || dst_entries_get_slow(&ipv4_dst_ops) < ipv4_dst_ops.gc_thresh) expire = ip_rt_gc_timeout; -#if RT_CACHE_DEBUG >= 2 - printk(KERN_DEBUG "expire++ %u %d %d %d\n", expire, - dst_entries_get_fast(&ipv4_dst_ops), goal, rover); -#endif out: return 0; } @@ -1179,16 +1171,6 @@ restart: rt->dst.rt_next = rt_hash_table[hash].chain; -#if RT_CACHE_DEBUG >= 2 - if (rt->dst.rt_next) { - struct rtable *trt; - printk(KERN_DEBUG "rt_cache @%02x: %pI4", - hash, &rt->rt_dst); - for (trt = rt->dst.rt_next; trt; trt = trt->dst.rt_next) - printk(" . %pI4", &trt->rt_dst); - printk("\n"); - } -#endif /* * Since lookup is lockfree, we must make sure * previous writes to rt are committed to memory @@ -1211,11 +1193,11 @@ static u32 rt_peer_genid(void) return atomic_read(&__rt_peer_genid); } -void rt_bind_peer(struct rtable *rt, int create) +void rt_bind_peer(struct rtable *rt, __be32 daddr, int create) { struct inet_peer *peer; - peer = inet_getpeer_v4(rt->rt_dst, create); + peer = inet_getpeer_v4(daddr, create); if (peer && cmpxchg(&rt->peer, NULL, peer) != NULL) inet_putpeer(peer); @@ -1249,7 +1231,7 @@ void __ip_select_ident(struct iphdr *iph, struct dst_entry *dst, int more) if (rt) { if (rt->peer == NULL) - rt_bind_peer(rt, 1); + rt_bind_peer(rt, rt->rt_dst, 1); /* If peer is attached to destination, it is never detached, so that we need not to grab a lock to dereference it. @@ -1347,10 +1329,6 @@ static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst) unsigned hash = rt_hash(rt->rt_key_dst, rt->rt_key_src, rt->rt_oif, rt_genid(dev_net(dst->dev))); -#if RT_CACHE_DEBUG >= 1 - printk(KERN_DEBUG "ipv4_negative_advice: redirect to %pI4/%02x dropped\n", - &rt->rt_dst, rt->rt_tos); -#endif rt_del(hash, rt); ret = NULL; } else if (rt->peer && @@ -1399,7 +1377,7 @@ void ip_rt_send_redirect(struct sk_buff *skb) rcu_read_unlock(); if (!rt->peer) - rt_bind_peer(rt, 1); + rt_bind_peer(rt, rt->rt_dst, 1); peer = rt->peer; if (!peer) { icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, rt->rt_gateway); @@ -1435,7 +1413,7 @@ void ip_rt_send_redirect(struct sk_buff *skb) peer->rate_tokens == ip_rt_redirect_number && net_ratelimit()) printk(KERN_WARNING "host %pI4/if%d ignores redirects for %pI4 to %pI4.\n", - &rt->rt_src, rt->rt_iif, + &ip_hdr(skb)->saddr, rt->rt_iif, &rt->rt_dst, &rt->rt_gateway); #endif } @@ -1467,7 +1445,7 @@ static int ip_error(struct sk_buff *skb) } if (!rt->peer) - rt_bind_peer(rt, 1); + rt_bind_peer(rt, rt->rt_dst, 1); peer = rt->peer; send = true; @@ -1507,7 +1485,7 @@ static inline unsigned short guess_mtu(unsigned short old_mtu) return 68; } -unsigned short ip_rt_frag_needed(struct net *net, struct iphdr *iph, +unsigned short ip_rt_frag_needed(struct net *net, const struct iphdr *iph, unsigned short new_mtu, struct net_device *dev) { @@ -1574,7 +1552,7 @@ static void ip_rt_update_pmtu(struct dst_entry *dst, u32 mtu) dst_confirm(dst); if (!rt->peer) - rt_bind_peer(rt, 1); + rt_bind_peer(rt, rt->rt_dst, 1); peer = rt->peer; if (peer) { if (mtu < ip_rt_min_pmtu) @@ -1631,7 +1609,7 @@ static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie) struct inet_peer *peer; if (!rt->peer) - rt_bind_peer(rt, 0); + rt_bind_peer(rt, rt->rt_dst, 0); peer = rt->peer; if (peer && peer->pmtu_expires) @@ -1687,6 +1665,7 @@ static int ip_rt_bug(struct sk_buff *skb) &ip_hdr(skb)->saddr, &ip_hdr(skb)->daddr, skb->dev ? skb->dev->name : "?"); kfree_skb(skb); + WARN_ON(1); return 0; } @@ -1699,22 +1678,26 @@ static int ip_rt_bug(struct sk_buff *skb) in IP options! */ -void ip_rt_get_source(u8 *addr, struct rtable *rt) +void ip_rt_get_source(u8 *addr, struct sk_buff *skb, struct rtable *rt) { __be32 src; - struct fib_result res; if (rt_is_output_route(rt)) - src = rt->rt_src; + src = ip_hdr(skb)->saddr; else { - struct flowi4 fl4 = { - .daddr = rt->rt_key_dst, - .saddr = rt->rt_key_src, - .flowi4_tos = rt->rt_tos, - .flowi4_oif = rt->rt_oif, - .flowi4_iif = rt->rt_iif, - .flowi4_mark = rt->rt_mark, - }; + struct fib_result res; + struct flowi4 fl4; + struct iphdr *iph; + + iph = ip_hdr(skb); + + memset(&fl4, 0, sizeof(fl4)); + fl4.daddr = iph->daddr; + fl4.saddr = iph->saddr; + fl4.flowi4_tos = iph->tos; + fl4.flowi4_oif = rt->dst.dev->ifindex; + fl4.flowi4_iif = skb->dev->ifindex; + fl4.flowi4_mark = skb->mark; rcu_read_lock(); if (fib_lookup(dev_net(rt->dst.dev), &fl4, &res) == 0) @@ -1767,7 +1750,7 @@ static unsigned int ipv4_default_mtu(const struct dst_entry *dst) return mtu; } -static void rt_init_metrics(struct rtable *rt, const struct flowi4 *oldflp4, +static void rt_init_metrics(struct rtable *rt, const struct flowi4 *fl4, struct fib_info *fi) { struct inet_peer *peer; @@ -1776,7 +1759,7 @@ static void rt_init_metrics(struct rtable *rt, const struct flowi4 *oldflp4, /* If a peer entry exists for this destination, we must hook * it up in order to get at cached metrics. */ - if (oldflp4 && (oldflp4->flowi4_flags & FLOWI_FLAG_PRECOW_METRICS)) + if (fl4 && (fl4->flowi4_flags & FLOWI_FLAG_PRECOW_METRICS)) create = 1; rt->peer = peer = inet_getpeer_v4(rt->rt_dst, create); @@ -1803,7 +1786,7 @@ static void rt_init_metrics(struct rtable *rt, const struct flowi4 *oldflp4, } } -static void rt_set_nexthop(struct rtable *rt, const struct flowi4 *oldflp4, +static void rt_set_nexthop(struct rtable *rt, const struct flowi4 *fl4, const struct fib_result *res, struct fib_info *fi, u16 type, u32 itag) { @@ -1813,7 +1796,7 @@ static void rt_set_nexthop(struct rtable *rt, const struct flowi4 *oldflp4, if (FIB_RES_GW(*res) && FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK) rt->rt_gateway = FIB_RES_GW(*res); - rt_init_metrics(rt, oldflp4, fi); + rt_init_metrics(rt, fl4, fi); #ifdef CONFIG_IP_ROUTE_CLASSID dst->tclassid = FIB_RES_NH(*res).nh_tclassid; #endif @@ -1830,20 +1813,15 @@ static void rt_set_nexthop(struct rtable *rt, const struct flowi4 *oldflp4, #endif set_class_tag(rt, itag); #endif - rt->rt_type = type; } -static struct rtable *rt_dst_alloc(bool nopolicy, bool noxfrm) +static struct rtable *rt_dst_alloc(struct net_device *dev, + bool nopolicy, bool noxfrm) { - struct rtable *rt = dst_alloc(&ipv4_dst_ops, 1); - if (rt) { - rt->dst.obsolete = -1; - - rt->dst.flags = DST_HOST | - (nopolicy ? DST_NOPOLICY : 0) | - (noxfrm ? DST_NOXFRM : 0); - } - return rt; + return dst_alloc(&ipv4_dst_ops, dev, 1, -1, + DST_HOST | + (nopolicy ? DST_NOPOLICY : 0) | + (noxfrm ? DST_NOXFRM : 0)); } /* called in rcu_read_lock() section */ @@ -1876,31 +1854,33 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, if (err < 0) goto e_err; } - rth = rt_dst_alloc(IN_DEV_CONF_GET(in_dev, NOPOLICY), false); + rth = rt_dst_alloc(init_net.loopback_dev, + IN_DEV_CONF_GET(in_dev, NOPOLICY), false); if (!rth) goto e_nobufs; +#ifdef CONFIG_IP_ROUTE_CLASSID + rth->dst.tclassid = itag; +#endif rth->dst.output = ip_rt_bug; rth->rt_key_dst = daddr; - rth->rt_dst = daddr; - rth->rt_tos = tos; - rth->rt_mark = skb->mark; rth->rt_key_src = saddr; + rth->rt_genid = rt_genid(dev_net(dev)); + rth->rt_flags = RTCF_MULTICAST; + rth->rt_type = RTN_MULTICAST; + rth->rt_key_tos = tos; + rth->rt_dst = daddr; rth->rt_src = saddr; -#ifdef CONFIG_IP_ROUTE_CLASSID - rth->dst.tclassid = itag; -#endif rth->rt_route_iif = dev->ifindex; rth->rt_iif = dev->ifindex; - rth->dst.dev = init_net.loopback_dev; - dev_hold(rth->dst.dev); rth->rt_oif = 0; + rth->rt_mark = skb->mark; rth->rt_gateway = daddr; rth->rt_spec_dst= spec_dst; - rth->rt_genid = rt_genid(dev_net(dev)); - rth->rt_flags = RTCF_MULTICAST; - rth->rt_type = RTN_MULTICAST; + rth->rt_peer_genid = 0; + rth->peer = NULL; + rth->fi = NULL; if (our) { rth->dst.input= ip_local_deliver; rth->rt_flags |= RTCF_LOCAL; @@ -2013,7 +1993,8 @@ static int __mkroute_input(struct sk_buff *skb, } } - rth = rt_dst_alloc(IN_DEV_CONF_GET(in_dev, NOPOLICY), + rth = rt_dst_alloc(out_dev->dev, + IN_DEV_CONF_GET(in_dev, NOPOLICY), IN_DEV_CONF_GET(out_dev, NOXFRM)); if (!rth) { err = -ENOBUFS; @@ -2021,27 +2002,28 @@ static int __mkroute_input(struct sk_buff *skb, } rth->rt_key_dst = daddr; - rth->rt_dst = daddr; - rth->rt_tos = tos; - rth->rt_mark = skb->mark; rth->rt_key_src = saddr; + rth->rt_genid = rt_genid(dev_net(rth->dst.dev)); + rth->rt_flags = flags; + rth->rt_type = res->type; + rth->rt_key_tos = tos; + rth->rt_dst = daddr; rth->rt_src = saddr; - rth->rt_gateway = daddr; rth->rt_route_iif = in_dev->dev->ifindex; rth->rt_iif = in_dev->dev->ifindex; - rth->dst.dev = (out_dev)->dev; - dev_hold(rth->dst.dev); rth->rt_oif = 0; + rth->rt_mark = skb->mark; + rth->rt_gateway = daddr; rth->rt_spec_dst= spec_dst; + rth->rt_peer_genid = 0; + rth->peer = NULL; + rth->fi = NULL; rth->dst.input = ip_forward; rth->dst.output = ip_output; - rth->rt_genid = rt_genid(dev_net(rth->dst.dev)); rt_set_nexthop(rth, NULL, res, res->fi, res->type, itag); - rth->rt_flags = flags; - *result = rth; err = 0; cleanup: @@ -2188,36 +2170,42 @@ brd_input: RT_CACHE_STAT_INC(in_brd); local_input: - rth = rt_dst_alloc(IN_DEV_CONF_GET(in_dev, NOPOLICY), false); + rth = rt_dst_alloc(net->loopback_dev, + IN_DEV_CONF_GET(in_dev, NOPOLICY), false); if (!rth) goto e_nobufs; + rth->dst.input= ip_local_deliver; rth->dst.output= ip_rt_bug; - rth->rt_genid = rt_genid(net); +#ifdef CONFIG_IP_ROUTE_CLASSID + rth->dst.tclassid = itag; +#endif rth->rt_key_dst = daddr; - rth->rt_dst = daddr; - rth->rt_tos = tos; - rth->rt_mark = skb->mark; rth->rt_key_src = saddr; + rth->rt_genid = rt_genid(net); + rth->rt_flags = flags|RTCF_LOCAL; + rth->rt_type = res.type; + rth->rt_key_tos = tos; + rth->rt_dst = daddr; rth->rt_src = saddr; #ifdef CONFIG_IP_ROUTE_CLASSID rth->dst.tclassid = itag; #endif rth->rt_route_iif = dev->ifindex; rth->rt_iif = dev->ifindex; - rth->dst.dev = net->loopback_dev; - dev_hold(rth->dst.dev); + rth->rt_oif = 0; + rth->rt_mark = skb->mark; rth->rt_gateway = daddr; rth->rt_spec_dst= spec_dst; - rth->dst.input= ip_local_deliver; - rth->rt_flags = flags|RTCF_LOCAL; + rth->rt_peer_genid = 0; + rth->peer = NULL; + rth->fi = NULL; if (res.type == RTN_UNREACHABLE) { rth->dst.input= ip_error; rth->dst.error= -err; rth->rt_flags &= ~RTCF_LOCAL; } - rth->rt_type = res.type; hash = rt_hash(daddr, saddr, fl4.flowi4_iif, rt_genid(net)); rth = rt_intern_hash(hash, rth, skb, fl4.flowi4_iif); err = 0; @@ -2288,7 +2276,7 @@ int ip_route_input_common(struct sk_buff *skb, __be32 daddr, __be32 saddr, ((__force u32)rth->rt_key_src ^ (__force u32)saddr) | (rth->rt_iif ^ iif) | rth->rt_oif | - (rth->rt_tos ^ tos)) == 0 && + (rth->rt_key_tos ^ tos)) == 0 && rth->rt_mark == skb->mark && net_eq(dev_net(rth->dst.dev), net) && !rt_is_expired(rth)) { @@ -2349,12 +2337,12 @@ EXPORT_SYMBOL(ip_route_input_common); /* called with rcu_read_lock() */ static struct rtable *__mkroute_output(const struct fib_result *res, const struct flowi4 *fl4, - const struct flowi4 *oldflp4, - struct net_device *dev_out, + __be32 orig_daddr, __be32 orig_saddr, + int orig_oif, struct net_device *dev_out, unsigned int flags) { struct fib_info *fi = res->fi; - u32 tos = RT_FL_TOS(oldflp4); + u32 tos = RT_FL_TOS(fl4); struct in_device *in_dev; u16 type = res->type; struct rtable *rth; @@ -2381,8 +2369,8 @@ static struct rtable *__mkroute_output(const struct fib_result *res, fi = NULL; } else if (type == RTN_MULTICAST) { flags |= RTCF_MULTICAST | RTCF_LOCAL; - if (!ip_check_mc_rcu(in_dev, oldflp4->daddr, oldflp4->saddr, - oldflp4->flowi4_proto)) + if (!ip_check_mc_rcu(in_dev, fl4->daddr, fl4->saddr, + fl4->flowi4_proto)) flags &= ~RTCF_LOCAL; /* If multicast route do not exist use * default one, but do not gateway in this case. @@ -2392,29 +2380,31 @@ static struct rtable *__mkroute_output(const struct fib_result *res, fi = NULL; } - rth = rt_dst_alloc(IN_DEV_CONF_GET(in_dev, NOPOLICY), + rth = rt_dst_alloc(dev_out, + IN_DEV_CONF_GET(in_dev, NOPOLICY), IN_DEV_CONF_GET(in_dev, NOXFRM)); if (!rth) return ERR_PTR(-ENOBUFS); - rth->rt_key_dst = oldflp4->daddr; - rth->rt_tos = tos; - rth->rt_key_src = oldflp4->saddr; - rth->rt_oif = oldflp4->flowi4_oif; - rth->rt_mark = oldflp4->flowi4_mark; + rth->dst.output = ip_output; + + rth->rt_key_dst = orig_daddr; + rth->rt_key_src = orig_saddr; + rth->rt_genid = rt_genid(dev_net(dev_out)); + rth->rt_flags = flags; + rth->rt_type = type; + rth->rt_key_tos = tos; rth->rt_dst = fl4->daddr; rth->rt_src = fl4->saddr; rth->rt_route_iif = 0; - rth->rt_iif = oldflp4->flowi4_oif ? : dev_out->ifindex; - /* get references to the devices that are to be hold by the routing - cache entry */ - rth->dst.dev = dev_out; - dev_hold(dev_out); + rth->rt_iif = orig_oif ? : dev_out->ifindex; + rth->rt_oif = orig_oif; + rth->rt_mark = fl4->flowi4_mark; rth->rt_gateway = fl4->daddr; rth->rt_spec_dst= fl4->saddr; - - rth->dst.output=ip_output; - rth->rt_genid = rt_genid(dev_net(dev_out)); + rth->rt_peer_genid = 0; + rth->peer = NULL; + rth->fi = NULL; RT_CACHE_STAT_INC(out_slow_tot); @@ -2432,7 +2422,7 @@ static struct rtable *__mkroute_output(const struct fib_result *res, #ifdef CONFIG_IP_MROUTE if (type == RTN_MULTICAST) { if (IN_DEV_MFORWARD(in_dev) && - !ipv4_is_local_multicast(oldflp4->daddr)) { + !ipv4_is_local_multicast(fl4->daddr)) { rth->dst.input = ip_mr_input; rth->dst.output = ip_mc_output; } @@ -2440,9 +2430,8 @@ static struct rtable *__mkroute_output(const struct fib_result *res, #endif } - rt_set_nexthop(rth, oldflp4, res, fi, type, 0); + rt_set_nexthop(rth, fl4, res, fi, type, 0); - rth->rt_flags = flags; return rth; } @@ -2451,36 +2440,37 @@ static struct rtable *__mkroute_output(const struct fib_result *res, * called with rcu_read_lock(); */ -static struct rtable *ip_route_output_slow(struct net *net, - const struct flowi4 *oldflp4) +static struct rtable *ip_route_output_slow(struct net *net, struct flowi4 *fl4) { - u32 tos = RT_FL_TOS(oldflp4); - struct flowi4 fl4; - struct fib_result res; - unsigned int flags = 0; struct net_device *dev_out = NULL; + u32 tos = RT_FL_TOS(fl4); + unsigned int flags = 0; + struct fib_result res; struct rtable *rth; + __be32 orig_daddr; + __be32 orig_saddr; + int orig_oif; res.fi = NULL; #ifdef CONFIG_IP_MULTIPLE_TABLES res.r = NULL; #endif - fl4.flowi4_oif = oldflp4->flowi4_oif; - fl4.flowi4_iif = net->loopback_dev->ifindex; - fl4.flowi4_mark = oldflp4->flowi4_mark; - fl4.daddr = oldflp4->daddr; - fl4.saddr = oldflp4->saddr; - fl4.flowi4_tos = tos & IPTOS_RT_MASK; - fl4.flowi4_scope = ((tos & RTO_ONLINK) ? - RT_SCOPE_LINK : RT_SCOPE_UNIVERSE); + orig_daddr = fl4->daddr; + orig_saddr = fl4->saddr; + orig_oif = fl4->flowi4_oif; + + fl4->flowi4_iif = net->loopback_dev->ifindex; + fl4->flowi4_tos = tos & IPTOS_RT_MASK; + fl4->flowi4_scope = ((tos & RTO_ONLINK) ? + RT_SCOPE_LINK : RT_SCOPE_UNIVERSE); rcu_read_lock(); - if (oldflp4->saddr) { + if (fl4->saddr) { rth = ERR_PTR(-EINVAL); - if (ipv4_is_multicast(oldflp4->saddr) || - ipv4_is_lbcast(oldflp4->saddr) || - ipv4_is_zeronet(oldflp4->saddr)) + if (ipv4_is_multicast(fl4->saddr) || + ipv4_is_lbcast(fl4->saddr) || + ipv4_is_zeronet(fl4->saddr)) goto out; /* I removed check for oif == dev_out->oif here. @@ -2491,11 +2481,11 @@ static struct rtable *ip_route_output_slow(struct net *net, of another iface. --ANK */ - if (oldflp4->flowi4_oif == 0 && - (ipv4_is_multicast(oldflp4->daddr) || - ipv4_is_lbcast(oldflp4->daddr))) { + if (fl4->flowi4_oif == 0 && + (ipv4_is_multicast(fl4->daddr) || + ipv4_is_lbcast(fl4->daddr))) { /* It is equivalent to inet_addr_type(saddr) == RTN_LOCAL */ - dev_out = __ip_dev_find(net, oldflp4->saddr, false); + dev_out = __ip_dev_find(net, fl4->saddr, false); if (dev_out == NULL) goto out; @@ -2514,20 +2504,20 @@ static struct rtable *ip_route_output_slow(struct net *net, Luckily, this hack is good workaround. */ - fl4.flowi4_oif = dev_out->ifindex; + fl4->flowi4_oif = dev_out->ifindex; goto make_route; } - if (!(oldflp4->flowi4_flags & FLOWI_FLAG_ANYSRC)) { + if (!(fl4->flowi4_flags & FLOWI_FLAG_ANYSRC)) { /* It is equivalent to inet_addr_type(saddr) == RTN_LOCAL */ - if (!__ip_dev_find(net, oldflp4->saddr, false)) + if (!__ip_dev_find(net, fl4->saddr, false)) goto out; } } - if (oldflp4->flowi4_oif) { - dev_out = dev_get_by_index_rcu(net, oldflp4->flowi4_oif); + if (fl4->flowi4_oif) { + dev_out = dev_get_by_index_rcu(net, fl4->flowi4_oif); rth = ERR_PTR(-ENODEV); if (dev_out == NULL) goto out; @@ -2537,37 +2527,37 @@ static struct rtable *ip_route_output_slow(struct net *net, rth = ERR_PTR(-ENETUNREACH); goto out; } - if (ipv4_is_local_multicast(oldflp4->daddr) || - ipv4_is_lbcast(oldflp4->daddr)) { - if (!fl4.saddr) - fl4.saddr = inet_select_addr(dev_out, 0, - RT_SCOPE_LINK); + if (ipv4_is_local_multicast(fl4->daddr) || + ipv4_is_lbcast(fl4->daddr)) { + if (!fl4->saddr) + fl4->saddr = inet_select_addr(dev_out, 0, + RT_SCOPE_LINK); goto make_route; } - if (!fl4.saddr) { - if (ipv4_is_multicast(oldflp4->daddr)) - fl4.saddr = inet_select_addr(dev_out, 0, - fl4.flowi4_scope); - else if (!oldflp4->daddr) - fl4.saddr = inet_select_addr(dev_out, 0, - RT_SCOPE_HOST); + if (fl4->saddr) { + if (ipv4_is_multicast(fl4->daddr)) + fl4->saddr = inet_select_addr(dev_out, 0, + fl4->flowi4_scope); + else if (!fl4->daddr) + fl4->saddr = inet_select_addr(dev_out, 0, + RT_SCOPE_HOST); } } - if (!fl4.daddr) { - fl4.daddr = fl4.saddr; - if (!fl4.daddr) - fl4.daddr = fl4.saddr = htonl(INADDR_LOOPBACK); + if (!fl4->daddr) { + fl4->daddr = fl4->saddr; + if (!fl4->daddr) + fl4->daddr = fl4->saddr = htonl(INADDR_LOOPBACK); dev_out = net->loopback_dev; - fl4.flowi4_oif = net->loopback_dev->ifindex; + fl4->flowi4_oif = net->loopback_dev->ifindex; res.type = RTN_LOCAL; flags |= RTCF_LOCAL; goto make_route; } - if (fib_lookup(net, &fl4, &res)) { + if (fib_lookup(net, fl4, &res)) { res.fi = NULL; - if (oldflp4->flowi4_oif) { + if (fl4->flowi4_oif) { /* Apparently, routing tables are wrong. Assume, that the destination is on link. @@ -2586,9 +2576,9 @@ static struct rtable *ip_route_output_slow(struct net *net, likely IPv6, but we do not. */ - if (fl4.saddr == 0) - fl4.saddr = inet_select_addr(dev_out, 0, - RT_SCOPE_LINK); + if (fl4->saddr == 0) + fl4->saddr = inet_select_addr(dev_out, 0, + RT_SCOPE_LINK); res.type = RTN_UNICAST; goto make_route; } @@ -2597,44 +2587,45 @@ static struct rtable *ip_route_output_slow(struct net *net, } if (res.type == RTN_LOCAL) { - if (!fl4.saddr) { + if (!fl4->saddr) { if (res.fi->fib_prefsrc) - fl4.saddr = res.fi->fib_prefsrc; + fl4->saddr = res.fi->fib_prefsrc; else - fl4.saddr = fl4.daddr; + fl4->saddr = fl4->daddr; } dev_out = net->loopback_dev; - fl4.flowi4_oif = dev_out->ifindex; + fl4->flowi4_oif = dev_out->ifindex; res.fi = NULL; flags |= RTCF_LOCAL; goto make_route; } #ifdef CONFIG_IP_ROUTE_MULTIPATH - if (res.fi->fib_nhs > 1 && fl4.flowi4_oif == 0) + if (res.fi->fib_nhs > 1 && fl4->flowi4_oif == 0) fib_select_multipath(&res); else #endif if (!res.prefixlen && res.table->tb_num_default > 1 && - res.type == RTN_UNICAST && !fl4.flowi4_oif) + res.type == RTN_UNICAST && !fl4->flowi4_oif) fib_select_default(&res); - if (!fl4.saddr) - fl4.saddr = FIB_RES_PREFSRC(net, res); + if (!fl4->saddr) + fl4->saddr = FIB_RES_PREFSRC(net, res); dev_out = FIB_RES_DEV(res); - fl4.flowi4_oif = dev_out->ifindex; + fl4->flowi4_oif = dev_out->ifindex; make_route: - rth = __mkroute_output(&res, &fl4, oldflp4, dev_out, flags); + rth = __mkroute_output(&res, fl4, orig_daddr, orig_saddr, orig_oif, + dev_out, flags); if (!IS_ERR(rth)) { unsigned int hash; - hash = rt_hash(oldflp4->daddr, oldflp4->saddr, oldflp4->flowi4_oif, + hash = rt_hash(orig_daddr, orig_saddr, orig_oif, rt_genid(dev_net(dev_out))); - rth = rt_intern_hash(hash, rth, NULL, oldflp4->flowi4_oif); + rth = rt_intern_hash(hash, rth, NULL, orig_oif); } out: @@ -2642,7 +2633,7 @@ out: return rth; } -struct rtable *__ip_route_output_key(struct net *net, const struct flowi4 *flp4) +struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *flp4) { struct rtable *rth; unsigned int hash; @@ -2660,13 +2651,17 @@ struct rtable *__ip_route_output_key(struct net *net, const struct flowi4 *flp4) rt_is_output_route(rth) && rth->rt_oif == flp4->flowi4_oif && rth->rt_mark == flp4->flowi4_mark && - !((rth->rt_tos ^ flp4->flowi4_tos) & + !((rth->rt_key_tos ^ flp4->flowi4_tos) & (IPTOS_RT_MASK | RTO_ONLINK)) && net_eq(dev_net(rth->dst.dev), net) && !rt_is_expired(rth)) { dst_use(&rth->dst, jiffies); RT_CACHE_STAT_INC(out_hit); rcu_read_unlock_bh(); + if (!flp4->saddr) + flp4->saddr = rth->rt_src; + if (!flp4->daddr) + flp4->daddr = rth->rt_dst; return rth; } RT_CACHE_STAT_INC(out_hlist_search); @@ -2692,6 +2687,12 @@ static void ipv4_rt_blackhole_update_pmtu(struct dst_entry *dst, u32 mtu) { } +static u32 *ipv4_rt_blackhole_cow_metrics(struct dst_entry *dst, + unsigned long old) +{ + return NULL; +} + static struct dst_ops ipv4_dst_blackhole_ops = { .family = AF_INET, .protocol = cpu_to_be16(ETH_P_IP), @@ -2700,11 +2701,12 @@ static struct dst_ops ipv4_dst_blackhole_ops = { .default_mtu = ipv4_blackhole_default_mtu, .default_advmss = ipv4_default_advmss, .update_pmtu = ipv4_rt_blackhole_update_pmtu, + .cow_metrics = ipv4_rt_blackhole_cow_metrics, }; struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_orig) { - struct rtable *rt = dst_alloc(&ipv4_dst_blackhole_ops, 1); + struct rtable *rt = dst_alloc(&ipv4_dst_blackhole_ops, NULL, 1, 0, 0); struct rtable *ort = (struct rtable *) dst_orig; if (rt) { @@ -2721,7 +2723,7 @@ struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_or rt->rt_key_dst = ort->rt_key_dst; rt->rt_key_src = ort->rt_key_src; - rt->rt_tos = ort->rt_tos; + rt->rt_key_tos = ort->rt_key_tos; rt->rt_route_iif = ort->rt_route_iif; rt->rt_iif = ort->rt_iif; rt->rt_oif = ort->rt_oif; @@ -2757,15 +2759,10 @@ struct rtable *ip_route_output_flow(struct net *net, struct flowi4 *flp4, if (IS_ERR(rt)) return rt; - if (flp4->flowi4_proto) { - if (!flp4->saddr) - flp4->saddr = rt->rt_src; - if (!flp4->daddr) - flp4->daddr = rt->rt_dst; + if (flp4->flowi4_proto) rt = (struct rtable *) xfrm_lookup(net, &rt->dst, flowi4_to_flowi(flp4), sk, 0); - } return rt; } @@ -2789,7 +2786,7 @@ static int rt_fill_info(struct net *net, r->rtm_family = AF_INET; r->rtm_dst_len = 32; r->rtm_src_len = 0; - r->rtm_tos = rt->rt_tos; + r->rtm_tos = rt->rt_key_tos; r->rtm_table = RT_TABLE_MAIN; NLA_PUT_U32(skb, RTA_TABLE, RT_TABLE_MAIN); r->rtm_type = rt->rt_type; @@ -2843,7 +2840,9 @@ static int rt_fill_info(struct net *net, if (ipv4_is_multicast(dst) && !ipv4_is_local_multicast(dst) && IPV4_DEVCONF_ALL(net, MC_FORWARDING)) { - int err = ipmr_get_route(net, skb, r, nowait); + int err = ipmr_get_route(net, skb, + rt->rt_src, rt->rt_dst, + r, nowait); if (err <= 0) { if (!nowait) { if (err == 0) @@ -3296,7 +3295,7 @@ int __init ip_rt_init(void) xfrm_init(); xfrm4_init(ip_rt_max_size); #endif - rtnl_register(PF_INET, RTM_GETROUTE, inet_rtm_getroute, NULL); + rtnl_register(PF_INET, RTM_GETROUTE, inet_rtm_getroute, NULL, NULL); #ifdef CONFIG_SYSCTL register_pernet_subsys(&sysctl_route_ops); diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 71e02969190..92bb9434b33 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -316,15 +316,16 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, ireq->wscale_ok = tcp_opt.wscale_ok; ireq->tstamp_ok = tcp_opt.saw_tstamp; req->ts_recent = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsval : 0; + treq->snt_synack = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsecr : 0; /* We throwed the options of the initial SYN away, so we hope * the ACK carries the same options again (see RFC1122 4.2.3.8) */ if (opt && opt->optlen) { - int opt_size = sizeof(struct ip_options) + opt->optlen; + int opt_size = sizeof(struct ip_options_rcu) + opt->optlen; ireq->opt = kmalloc(opt_size, GFP_ATOMIC); - if (ireq->opt != NULL && ip_options_echo(ireq->opt, skb)) { + if (ireq->opt != NULL && ip_options_echo(&ireq->opt->opt, skb)) { kfree(ireq->opt); ireq->opt = NULL; } diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 321e6e84dbc..69fd7201129 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -13,6 +13,7 @@ #include <linux/seqlock.h> #include <linux/init.h> #include <linux/slab.h> +#include <linux/nsproxy.h> #include <net/snmp.h> #include <net/icmp.h> #include <net/ip.h> @@ -21,6 +22,7 @@ #include <net/udp.h> #include <net/cipso_ipv4.h> #include <net/inet_frag.h> +#include <net/ping.h> static int zero; static int tcp_retr1_max = 255; @@ -30,6 +32,8 @@ static int tcp_adv_win_scale_min = -31; static int tcp_adv_win_scale_max = 31; static int ip_ttl_min = 1; static int ip_ttl_max = 255; +static int ip_ping_group_range_min[] = { 0, 0 }; +static int ip_ping_group_range_max[] = { GID_T_MAX, GID_T_MAX }; /* Update system visible IP port range */ static void set_local_port_range(int range[2]) @@ -68,6 +72,53 @@ static int ipv4_local_port_range(ctl_table *table, int write, return ret; } + +void inet_get_ping_group_range_table(struct ctl_table *table, gid_t *low, gid_t *high) +{ + gid_t *data = table->data; + unsigned seq; + do { + seq = read_seqbegin(&sysctl_local_ports.lock); + + *low = data[0]; + *high = data[1]; + } while (read_seqretry(&sysctl_local_ports.lock, seq)); +} + +/* Update system visible IP port range */ +static void set_ping_group_range(struct ctl_table *table, int range[2]) +{ + gid_t *data = table->data; + write_seqlock(&sysctl_local_ports.lock); + data[0] = range[0]; + data[1] = range[1]; + write_sequnlock(&sysctl_local_ports.lock); +} + +/* Validate changes from /proc interface. */ +static int ipv4_ping_group_range(ctl_table *table, int write, + void __user *buffer, + size_t *lenp, loff_t *ppos) +{ + int ret; + gid_t range[2]; + ctl_table tmp = { + .data = &range, + .maxlen = sizeof(range), + .mode = table->mode, + .extra1 = &ip_ping_group_range_min, + .extra2 = &ip_ping_group_range_max, + }; + + inet_get_ping_group_range_table(table, range, range + 1); + ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); + + if (write && ret == 0) + set_ping_group_range(table, range); + + return ret; +} + static int proc_tcp_congestion_control(ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { @@ -347,20 +398,6 @@ static struct ctl_table ipv4_table[] = { .proc_handler = proc_dointvec_jiffies, }, { - .procname = "inet_peer_gc_mintime", - .data = &inet_peer_gc_mintime, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_jiffies, - }, - { - .procname = "inet_peer_gc_maxtime", - .data = &inet_peer_gc_maxtime, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_jiffies, - }, - { .procname = "tcp_orphan_retries", .data = &sysctl_tcp_orphan_retries, .maxlen = sizeof(int), @@ -677,6 +714,13 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "ping_group_range", + .data = &init_net.ipv4.sysctl_ping_group_range, + .maxlen = sizeof(init_net.ipv4.sysctl_ping_group_range), + .mode = 0644, + .proc_handler = ipv4_ping_group_range, + }, { } }; @@ -711,8 +755,18 @@ static __net_init int ipv4_sysctl_init_net(struct net *net) &net->ipv4.sysctl_icmp_ratemask; table[6].data = &net->ipv4.sysctl_rt_cache_rebuild_count; + table[7].data = + &net->ipv4.sysctl_ping_group_range; + } + /* + * Sane defaults - nobody may create ping sockets. + * Boot scripts should set this to distro-specific group. + */ + net->ipv4.sysctl_ping_group_range[0] = 1; + net->ipv4.sysctl_ping_group_range[1] = 0; + net->ipv4.sysctl_rt_cache_rebuild_count = 4; net->ipv4.ipv4_hdr = register_net_sysctl_table(net, diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c index 34340c9c95f..f376b05cca8 100644 --- a/net/ipv4/tcp_cubic.c +++ b/net/ipv4/tcp_cubic.c @@ -93,6 +93,7 @@ struct bictcp { u32 ack_cnt; /* number of acks */ u32 tcp_cwnd; /* estimated tcp cwnd */ #define ACK_RATIO_SHIFT 4 +#define ACK_RATIO_LIMIT (32u << ACK_RATIO_SHIFT) u16 delayed_ack; /* estimate the ratio of Packets/ACKs << 4 */ u8 sample_cnt; /* number of samples to decide curr_rtt */ u8 found; /* the exit point is found? */ @@ -398,8 +399,12 @@ static void bictcp_acked(struct sock *sk, u32 cnt, s32 rtt_us) u32 delay; if (icsk->icsk_ca_state == TCP_CA_Open) { - cnt -= ca->delayed_ack >> ACK_RATIO_SHIFT; - ca->delayed_ack += cnt; + u32 ratio = ca->delayed_ack; + + ratio -= ca->delayed_ack >> ACK_RATIO_SHIFT; + ratio += cnt; + + ca->delayed_ack = min(ratio, ACK_RATIO_LIMIT); } /* Some calls are for duplicates without timetamps */ diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index bef9f04c22b..ea0d2183df4 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -880,6 +880,11 @@ static void tcp_init_metrics(struct sock *sk) tp->snd_ssthresh = dst_metric(dst, RTAX_SSTHRESH); if (tp->snd_ssthresh > tp->snd_cwnd_clamp) tp->snd_ssthresh = tp->snd_cwnd_clamp; + } else { + /* ssthresh may have been reduced unnecessarily during. + * 3WHS. Restore it back to its initial default. + */ + tp->snd_ssthresh = TCP_INFINITE_SSTHRESH; } if (dst_metric(dst, RTAX_REORDERING) && tp->reordering != dst_metric(dst, RTAX_REORDERING)) { @@ -887,10 +892,7 @@ static void tcp_init_metrics(struct sock *sk) tp->reordering = dst_metric(dst, RTAX_REORDERING); } - if (dst_metric(dst, RTAX_RTT) == 0) - goto reset; - - if (!tp->srtt && dst_metric_rtt(dst, RTAX_RTT) < (TCP_TIMEOUT_INIT << 3)) + if (dst_metric(dst, RTAX_RTT) == 0 || tp->srtt == 0) goto reset; /* Initial rtt is determined from SYN,SYN-ACK. @@ -916,19 +918,26 @@ static void tcp_init_metrics(struct sock *sk) tp->mdev_max = tp->rttvar = max(tp->mdev, tcp_rto_min(sk)); } tcp_set_rto(sk); - if (inet_csk(sk)->icsk_rto < TCP_TIMEOUT_INIT && !tp->rx_opt.saw_tstamp) { reset: - /* Play conservative. If timestamps are not - * supported, TCP will fail to recalculate correct - * rtt, if initial rto is too small. FORGET ALL AND RESET! + if (tp->srtt == 0) { + /* RFC2988bis: We've failed to get a valid RTT sample from + * 3WHS. This is most likely due to retransmission, + * including spurious one. Reset the RTO back to 3secs + * from the more aggressive 1sec to avoid more spurious + * retransmission. */ - if (!tp->rx_opt.saw_tstamp && tp->srtt) { - tp->srtt = 0; - tp->mdev = tp->mdev_max = tp->rttvar = TCP_TIMEOUT_INIT; - inet_csk(sk)->icsk_rto = TCP_TIMEOUT_INIT; - } + tp->mdev = tp->mdev_max = tp->rttvar = TCP_TIMEOUT_FALLBACK; + inet_csk(sk)->icsk_rto = TCP_TIMEOUT_FALLBACK; } - tp->snd_cwnd = tcp_init_cwnd(tp, dst); + /* Cut cwnd down to 1 per RFC5681 if SYN or SYN-ACK has been + * retransmitted. In light of RFC2988bis' more aggressive 1sec + * initRTO, we only reset cwnd when more than 1 SYN/SYN-ACK + * retransmission has occurred. + */ + if (tp->total_retrans > 1) + tp->snd_cwnd = 1; + else + tp->snd_cwnd = tcp_init_cwnd(tp, dst); tp->snd_cwnd_stamp = tcp_time_stamp; } @@ -3112,12 +3121,13 @@ static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked, int flag) tcp_xmit_retransmit_queue(sk); } -static void tcp_valid_rtt_meas(struct sock *sk, u32 seq_rtt) +void tcp_valid_rtt_meas(struct sock *sk, u32 seq_rtt) { tcp_rtt_estimator(sk, seq_rtt); tcp_set_rto(sk); inet_csk(sk)->icsk_backoff = 0; } +EXPORT_SYMBOL(tcp_valid_rtt_meas); /* Read draft-ietf-tcplw-high-performance before mucking * with this code. (Supersedes RFC1323) @@ -5806,12 +5816,6 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, tp->rx_opt.snd_wscale; tcp_init_wl(tp, TCP_SKB_CB(skb)->seq); - /* tcp_ack considers this ACK as duplicate - * and does not calculate rtt. - * Force it here. - */ - tcp_ack_update_rtt(sk, 0, 0); - if (tp->rx_opt.tstamp_ok) tp->advmss -= TCPOLEN_TSTAMP_ALIGNED; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index f7e6c2c2d2b..617dee3ccfb 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -146,13 +146,15 @@ EXPORT_SYMBOL_GPL(tcp_twsk_unique); /* This will initiate an outgoing connection. */ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) { + struct sockaddr_in *usin = (struct sockaddr_in *)uaddr; struct inet_sock *inet = inet_sk(sk); struct tcp_sock *tp = tcp_sk(sk); - struct sockaddr_in *usin = (struct sockaddr_in *)uaddr; __be16 orig_sport, orig_dport; - struct rtable *rt; __be32 daddr, nexthop; + struct flowi4 *fl4; + struct rtable *rt; int err; + struct ip_options_rcu *inet_opt; if (addr_len < sizeof(struct sockaddr_in)) return -EINVAL; @@ -161,15 +163,18 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) return -EAFNOSUPPORT; nexthop = daddr = usin->sin_addr.s_addr; - if (inet->opt && inet->opt->srr) { + inet_opt = rcu_dereference_protected(inet->inet_opt, + sock_owned_by_user(sk)); + if (inet_opt && inet_opt->opt.srr) { if (!daddr) return -EINVAL; - nexthop = inet->opt->faddr; + nexthop = inet_opt->opt.faddr; } orig_sport = inet->inet_sport; orig_dport = usin->sin_port; - rt = ip_route_connect(nexthop, inet->inet_saddr, + fl4 = &inet->cork.fl.u.ip4; + rt = ip_route_connect(fl4, nexthop, inet->inet_saddr, RT_CONN_FLAGS(sk), sk->sk_bound_dev_if, IPPROTO_TCP, orig_sport, orig_dport, sk, true); @@ -185,11 +190,11 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) return -ENETUNREACH; } - if (!inet->opt || !inet->opt->srr) - daddr = rt->rt_dst; + if (!inet_opt || !inet_opt->opt.srr) + daddr = fl4->daddr; if (!inet->inet_saddr) - inet->inet_saddr = rt->rt_src; + inet->inet_saddr = fl4->saddr; inet->inet_rcv_saddr = inet->inet_saddr; if (tp->rx_opt.ts_recent_stamp && inet->inet_daddr != daddr) { @@ -200,8 +205,8 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) } if (tcp_death_row.sysctl_tw_recycle && - !tp->rx_opt.ts_recent_stamp && rt->rt_dst == daddr) { - struct inet_peer *peer = rt_get_peer(rt); + !tp->rx_opt.ts_recent_stamp && fl4->daddr == daddr) { + struct inet_peer *peer = rt_get_peer(rt, fl4->daddr); /* * VJ's idea. We save last timestamp seen from * the destination in peer table, when entering state @@ -221,8 +226,8 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) inet->inet_daddr = daddr; inet_csk(sk)->icsk_ext_hdr_len = 0; - if (inet->opt) - inet_csk(sk)->icsk_ext_hdr_len = inet->opt->optlen; + if (inet_opt) + inet_csk(sk)->icsk_ext_hdr_len = inet_opt->opt.optlen; tp->rx_opt.mss_clamp = TCP_MSS_DEFAULT; @@ -236,8 +241,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) if (err) goto failure; - rt = ip_route_newports(rt, IPPROTO_TCP, - orig_sport, orig_dport, + rt = ip_route_newports(fl4, rt, orig_sport, orig_dport, inet->inet_sport, inet->inet_dport, sk); if (IS_ERR(rt)) { err = PTR_ERR(rt); @@ -279,7 +283,7 @@ EXPORT_SYMBOL(tcp_v4_connect); /* * This routine does path mtu discovery as defined in RFC1191. */ -static void do_pmtu_discovery(struct sock *sk, struct iphdr *iph, u32 mtu) +static void do_pmtu_discovery(struct sock *sk, const struct iphdr *iph, u32 mtu) { struct dst_entry *dst; struct inet_sock *inet = inet_sk(sk); @@ -341,7 +345,7 @@ static void do_pmtu_discovery(struct sock *sk, struct iphdr *iph, u32 mtu) void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) { - struct iphdr *iph = (struct iphdr *)icmp_skb->data; + const struct iphdr *iph = (const struct iphdr *)icmp_skb->data; struct tcphdr *th = (struct tcphdr *)(icmp_skb->data + (iph->ihl << 2)); struct inet_connection_sock *icsk; struct tcp_sock *tp; @@ -425,8 +429,8 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) break; icsk->icsk_backoff--; - inet_csk(sk)->icsk_rto = __tcp_set_rto(tp) << - icsk->icsk_backoff; + inet_csk(sk)->icsk_rto = (tp->srtt ? __tcp_set_rto(tp) : + TCP_TIMEOUT_INIT) << icsk->icsk_backoff; tcp_bound_rto(sk); skb = tcp_write_queue_head(sk); @@ -647,7 +651,7 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) arg.flags = (sk && inet_sk(sk)->transparent) ? IP_REPLY_ARG_NOSRCCHECK : 0; net = dev_net(skb_dst(skb)->dev); - ip_send_reply(net->ipv4.tcp_sock, skb, + ip_send_reply(net->ipv4.tcp_sock, skb, ip_hdr(skb)->saddr, &arg, arg.iov[0].iov_len); TCP_INC_STATS_BH(net, TCP_MIB_OUTSEGS); @@ -722,7 +726,7 @@ static void tcp_v4_send_ack(struct sk_buff *skb, u32 seq, u32 ack, if (oif) arg.bound_dev_if = oif; - ip_send_reply(net->ipv4.tcp_sock, skb, + ip_send_reply(net->ipv4.tcp_sock, skb, ip_hdr(skb)->saddr, &arg, arg.iov[0].iov_len); TCP_INC_STATS_BH(net, TCP_MIB_OUTSEGS); @@ -765,11 +769,12 @@ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, struct request_values *rvp) { const struct inet_request_sock *ireq = inet_rsk(req); + struct flowi4 fl4; int err = -1; struct sk_buff * skb; /* First, grab a route. */ - if (!dst && (dst = inet_csk_route_req(sk, req)) == NULL) + if (!dst && (dst = inet_csk_route_req(sk, &fl4, req)) == NULL) return -1; skb = tcp_make_synack(sk, dst, req, rvp); @@ -820,17 +825,18 @@ static void syn_flood_warning(const struct sk_buff *skb) /* * Save and compile IPv4 options into the request_sock if needed. */ -static struct ip_options *tcp_v4_save_options(struct sock *sk, - struct sk_buff *skb) +static struct ip_options_rcu *tcp_v4_save_options(struct sock *sk, + struct sk_buff *skb) { - struct ip_options *opt = &(IPCB(skb)->opt); - struct ip_options *dopt = NULL; + const struct ip_options *opt = &(IPCB(skb)->opt); + struct ip_options_rcu *dopt = NULL; if (opt && opt->optlen) { - int opt_size = optlength(opt); + int opt_size = sizeof(*dopt) + opt->optlen; + dopt = kmalloc(opt_size, GFP_ATOMIC); if (dopt) { - if (ip_options_echo(dopt, skb)) { + if (ip_options_echo(&dopt->opt, skb)) { kfree(dopt); dopt = NULL; } @@ -1333,6 +1339,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) req->cookie_ts = tmp_opt.tstamp_ok; } else if (!isn) { struct inet_peer *peer = NULL; + struct flowi4 fl4; /* VJ's idea. We save last timestamp seen * from the destination in peer table, when entering @@ -1345,9 +1352,9 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) */ if (tmp_opt.saw_tstamp && tcp_death_row.sysctl_tw_recycle && - (dst = inet_csk_route_req(sk, req)) != NULL && - (peer = rt_get_peer((struct rtable *)dst)) != NULL && - peer->daddr.addr.a4 == saddr) { + (dst = inet_csk_route_req(sk, &fl4, req)) != NULL && + fl4.daddr == saddr && + (peer = rt_get_peer((struct rtable *)dst, fl4.daddr)) != NULL) { inet_peer_refcheck(peer); if ((u32)get_seconds() - peer->tcp_ts_stamp < TCP_PAWS_MSL && (s32)(peer->tcp_ts - req->ts_recent) > @@ -1377,6 +1384,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) isn = tcp_v4_init_sequence(skb); } tcp_rsk(req)->snt_isn = isn; + tcp_rsk(req)->snt_synack = tcp_time_stamp; if (tcp_v4_send_synack(sk, dst, req, (struct request_values *)&tmp_ext) || @@ -1411,19 +1419,16 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, #ifdef CONFIG_TCP_MD5SIG struct tcp_md5sig_key *key; #endif + struct ip_options_rcu *inet_opt; if (sk_acceptq_is_full(sk)) goto exit_overflow; - if (!dst && (dst = inet_csk_route_req(sk, req)) == NULL) - goto exit; - newsk = tcp_create_openreq_child(sk, req, skb); if (!newsk) goto exit_nonewsk; newsk->sk_gso_type = SKB_GSO_TCPV4; - sk_setup_caps(newsk, dst); newtp = tcp_sk(newsk); newinet = inet_sk(newsk); @@ -1431,15 +1436,21 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, newinet->inet_daddr = ireq->rmt_addr; newinet->inet_rcv_saddr = ireq->loc_addr; newinet->inet_saddr = ireq->loc_addr; - newinet->opt = ireq->opt; + inet_opt = ireq->opt; + rcu_assign_pointer(newinet->inet_opt, inet_opt); ireq->opt = NULL; newinet->mc_index = inet_iif(skb); newinet->mc_ttl = ip_hdr(skb)->ttl; inet_csk(newsk)->icsk_ext_hdr_len = 0; - if (newinet->opt) - inet_csk(newsk)->icsk_ext_hdr_len = newinet->opt->optlen; + if (inet_opt) + inet_csk(newsk)->icsk_ext_hdr_len = inet_opt->opt.optlen; newinet->inet_id = newtp->write_seq ^ jiffies; + if (!dst && (dst = inet_csk_route_child_sock(sk, newsk, req)) == NULL) + goto put_and_exit; + + sk_setup_caps(newsk, dst); + tcp_mtup_init(newsk); tcp_sync_mss(newsk, dst_mtu(dst)); newtp->advmss = dst_metric_advmss(dst); @@ -1448,6 +1459,10 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, newtp->advmss = tcp_sk(sk)->rx_opt.user_mss; tcp_initialize_rcv_mss(newsk); + if (tcp_rsk(req)->snt_synack) + tcp_valid_rtt_meas(newsk, + tcp_time_stamp - tcp_rsk(req)->snt_synack); + newtp->total_retrans = req->retrans; #ifdef CONFIG_TCP_MD5SIG /* Copy over the MD5 key from the original socket */ @@ -1467,10 +1482,8 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, } #endif - if (__inet_inherit_port(sk, newsk) < 0) { - sock_put(newsk); - goto exit; - } + if (__inet_inherit_port(sk, newsk) < 0) + goto put_and_exit; __inet_hash_nolisten(newsk, NULL); return newsk; @@ -1482,6 +1495,9 @@ exit_nonewsk: exit: NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); return NULL; +put_and_exit: + sock_put(newsk); + goto exit; } EXPORT_SYMBOL(tcp_v4_syn_recv_sock); @@ -1764,12 +1780,13 @@ struct inet_peer *tcp_v4_get_peer(struct sock *sk, bool *release_it) struct inet_sock *inet = inet_sk(sk); struct inet_peer *peer; - if (!rt || rt->rt_dst != inet->inet_daddr) { + if (!rt || + inet->cork.fl.u.ip4.daddr != inet->inet_daddr) { peer = inet_getpeer_v4(inet->inet_daddr, 1); *release_it = true; } else { if (!rt->peer) - rt_bind_peer(rt, 1); + rt_bind_peer(rt, inet->inet_daddr, 1); peer = rt->peer; *release_it = false; } @@ -1842,7 +1859,7 @@ static int tcp_v4_init_sock(struct sock *sk) * algorithms that we must have the following bandaid to talk * efficiently to them. -DaveM */ - tp->snd_cwnd = 2; + tp->snd_cwnd = TCP_INIT_CWND; /* See draft-stevens-tcpca-spec-01 for discussion of the * initialization of these values. @@ -2359,7 +2376,7 @@ static void get_openreq4(struct sock *sk, struct request_sock *req, int ttd = req->expires - jiffies; seq_printf(f, "%4d: %08X:%04X %08X:%04X" - " %02X %08X:%08X %02X:%08lX %08X %5d %8d %u %d %p%n", + " %02X %08X:%08X %02X:%08lX %08X %5d %8d %u %d %pK%n", i, ireq->loc_addr, ntohs(inet_sk(sk)->inet_sport), @@ -2414,7 +2431,7 @@ static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i, int *len) rx_queue = max_t(int, tp->rcv_nxt - tp->copied_seq, 0); seq_printf(f, "%4d: %08X:%04X %08X:%04X %02X %08X:%08X %02X:%08lX " - "%08X %5d %8d %lu %d %p %lu %lu %u %u %d%n", + "%08X %5d %8d %lu %d %pK %lu %lu %u %u %d%n", i, src, srcp, dest, destp, sk->sk_state, tp->write_seq - tp->snd_una, rx_queue, @@ -2449,7 +2466,7 @@ static void get_timewait4_sock(struct inet_timewait_sock *tw, srcp = ntohs(tw->tw_sport); seq_printf(f, "%4d: %08X:%04X %08X:%04X" - " %02X %08X:%08X %02X:%08lX %08X %5d %8d %d %d %p%n", + " %02X %08X:%08X %02X:%08lX %08X %5d %8d %d %d %pK%n", i, src, srcp, dest, destp, tw->tw_substate, 0, 0, 3, jiffies_to_clock_t(ttd), 0, 0, 0, 0, atomic_read(&tw->tw_refcnt), tw, len); @@ -2527,7 +2544,7 @@ void tcp4_proc_exit(void) struct sk_buff **tcp4_gro_receive(struct sk_buff **head, struct sk_buff *skb) { - struct iphdr *iph = skb_gro_network_header(skb); + const struct iphdr *iph = skb_gro_network_header(skb); switch (skb->ip_summed) { case CHECKSUM_COMPLETE: @@ -2548,7 +2565,7 @@ struct sk_buff **tcp4_gro_receive(struct sk_buff **head, struct sk_buff *skb) int tcp4_gro_complete(struct sk_buff *skb) { - struct iphdr *iph = ip_hdr(skb); + const struct iphdr *iph = ip_hdr(skb); struct tcphdr *th = tcp_hdr(skb); th->check = ~tcp_v4_check(skb->len - skb_transport_offset(skb), diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 80b1f80759a..d2fe4e06b47 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -486,7 +486,7 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, * algorithms that we must have the following bandaid to talk * efficiently to them. -DaveM */ - newtp->snd_cwnd = 2; + newtp->snd_cwnd = TCP_INIT_CWND; newtp->snd_cwnd_cnt = 0; newtp->bytes_acked = 0; @@ -720,6 +720,10 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPDEFERACCEPTDROP); return NULL; } + if (tmp_opt.saw_tstamp && tmp_opt.rcv_tsecr) + tcp_rsk(req)->snt_synack = tmp_opt.rcv_tsecr; + else if (req->retrans) /* don't take RTT sample if retrans && ~TS */ + tcp_rsk(req)->snt_synack = 0; /* OK, ACK is valid, create big socket and * feed this segment to it. It will repeat all diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 17388c7f49c..882e0b0964d 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -899,7 +899,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, TCP_ADD_STATS(sock_net(sk), TCP_MIB_OUTSEGS, tcp_skb_pcount(skb)); - err = icsk->icsk_af_ops->queue_xmit(skb); + err = icsk->icsk_af_ops->queue_xmit(skb, &inet->cork.fl); if (likely(err <= 0)) return err; diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index a15c8fb653a..abca870d8ff 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -578,7 +578,7 @@ found: void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable) { struct inet_sock *inet; - struct iphdr *iph = (struct iphdr *)skb->data; + const struct iphdr *iph = (const struct iphdr *)skb->data; struct udphdr *uh = (struct udphdr *)(skb->data+(iph->ihl<<2)); const int type = icmp_hdr(skb)->type; const int code = icmp_hdr(skb)->code; @@ -706,12 +706,11 @@ static void udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst) } } -static int udp_send_skb(struct sk_buff *skb, __be32 daddr, __be32 dport) +static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4) { struct sock *sk = skb->sk; struct inet_sock *inet = inet_sk(sk); struct udphdr *uh; - struct rtable *rt = (struct rtable *)skb_dst(skb); int err = 0; int is_udplite = IS_UDPLITE(sk); int offset = skb_transport_offset(skb); @@ -723,7 +722,7 @@ static int udp_send_skb(struct sk_buff *skb, __be32 daddr, __be32 dport) */ uh = udp_hdr(skb); uh->source = inet->inet_sport; - uh->dest = dport; + uh->dest = fl4->fl4_dport; uh->len = htons(len); uh->check = 0; @@ -737,14 +736,14 @@ static int udp_send_skb(struct sk_buff *skb, __be32 daddr, __be32 dport) } else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* UDP hardware csum */ - udp4_hwcsum(skb, rt->rt_src, daddr); + udp4_hwcsum(skb, fl4->saddr, fl4->daddr); goto send; } else csum = udp_csum(skb); /* add protocol-dependent pseudo-header */ - uh->check = csum_tcpudp_magic(rt->rt_src, daddr, len, + uh->check = csum_tcpudp_magic(fl4->saddr, fl4->daddr, len, sk->sk_protocol, csum); if (uh->check == 0) uh->check = CSUM_MANGLED_0; @@ -774,11 +773,11 @@ static int udp_push_pending_frames(struct sock *sk) struct sk_buff *skb; int err = 0; - skb = ip_finish_skb(sk); + skb = ip_finish_skb(sk, fl4); if (!skb) goto out; - err = udp_send_skb(skb, fl4->daddr, fl4->fl4_dport); + err = udp_send_skb(skb, fl4); out: up->len = 0; @@ -791,6 +790,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, { struct inet_sock *inet = inet_sk(sk); struct udp_sock *up = udp_sk(sk); + struct flowi4 fl4_stack; struct flowi4 *fl4; int ulen = len; struct ipcm_cookie ipc; @@ -804,6 +804,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, int corkreq = up->corkflag || msg->msg_flags&MSG_MORE; int (*getfrag)(void *, char *, int, int, int, struct sk_buff *); struct sk_buff *skb; + struct ip_options_data opt_copy; if (len > 0xFFFF) return -EMSGSIZE; @@ -820,6 +821,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, getfrag = is_udplite ? udplite_getfrag : ip_generic_getfrag; + fl4 = &inet->cork.fl.u.ip4; if (up->pending) { /* * There are pending frames. @@ -877,22 +879,32 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, free = 1; connected = 0; } - if (!ipc.opt) - ipc.opt = inet->opt; + if (!ipc.opt) { + struct ip_options_rcu *inet_opt; + + rcu_read_lock(); + inet_opt = rcu_dereference(inet->inet_opt); + if (inet_opt) { + memcpy(&opt_copy, inet_opt, + sizeof(*inet_opt) + inet_opt->opt.optlen); + ipc.opt = &opt_copy.opt; + } + rcu_read_unlock(); + } saddr = ipc.addr; ipc.addr = faddr = daddr; - if (ipc.opt && ipc.opt->srr) { + if (ipc.opt && ipc.opt->opt.srr) { if (!daddr) return -EINVAL; - faddr = ipc.opt->faddr; + faddr = ipc.opt->opt.faddr; connected = 0; } tos = RT_TOS(inet->tos); if (sock_flag(sk, SOCK_LOCALROUTE) || (msg->msg_flags & MSG_DONTROUTE) || - (ipc.opt && ipc.opt->is_strictroute)) { + (ipc.opt && ipc.opt->opt.is_strictroute)) { tos |= RTO_ONLINK; connected = 0; } @@ -909,16 +921,16 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, rt = (struct rtable *)sk_dst_check(sk, 0); if (rt == NULL) { - struct flowi4 fl4; struct net *net = sock_net(sk); - flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos, + fl4 = &fl4_stack; + flowi4_init_output(fl4, ipc.oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, sk->sk_protocol, inet_sk_flowi_flags(sk)|FLOWI_FLAG_CAN_SLEEP, faddr, saddr, dport, inet->inet_sport); - security_sk_classify_flow(sk, flowi4_to_flowi(&fl4)); - rt = ip_route_output_flow(net, &fl4, sk); + security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); + rt = ip_route_output_flow(net, fl4, sk); if (IS_ERR(rt)) { err = PTR_ERR(rt); rt = NULL; @@ -939,18 +951,18 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, goto do_confirm; back_from_confirm: - saddr = rt->rt_src; + saddr = fl4->saddr; if (!ipc.addr) - daddr = ipc.addr = rt->rt_dst; + daddr = ipc.addr = fl4->daddr; /* Lockless fast path for the non-corking case. */ if (!corkreq) { - skb = ip_make_skb(sk, getfrag, msg->msg_iov, ulen, + skb = ip_make_skb(sk, fl4, getfrag, msg->msg_iov, ulen, sizeof(struct udphdr), &ipc, &rt, msg->msg_flags); err = PTR_ERR(skb); if (skb && !IS_ERR(skb)) - err = udp_send_skb(skb, daddr, dport); + err = udp_send_skb(skb, fl4); goto out; } @@ -976,9 +988,9 @@ back_from_confirm: do_append_data: up->len += ulen; - err = ip_append_data(sk, getfrag, msg->msg_iov, ulen, - sizeof(struct udphdr), &ipc, &rt, - corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags); + err = ip_append_data(sk, fl4, getfrag, msg->msg_iov, ulen, + sizeof(struct udphdr), &ipc, &rt, + corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags); if (err) udp_flush_pending_frames(sk); else if (!corkreq) @@ -1018,6 +1030,7 @@ EXPORT_SYMBOL(udp_sendmsg); int udp_sendpage(struct sock *sk, struct page *page, int offset, size_t size, int flags) { + struct inet_sock *inet = inet_sk(sk); struct udp_sock *up = udp_sk(sk); int ret; @@ -1042,7 +1055,8 @@ int udp_sendpage(struct sock *sk, struct page *page, int offset, return -EINVAL; } - ret = ip_append_page(sk, page, offset, size, flags); + ret = ip_append_page(sk, &inet->cork.fl.u.ip4, + page, offset, size, flags); if (ret == -EOPNOTSUPP) { release_sock(sk); return sock_no_sendpage(sk->sk_socket, page, offset, @@ -2076,7 +2090,7 @@ static void udp4_format_sock(struct sock *sp, struct seq_file *f, __u16 srcp = ntohs(inet->inet_sport); seq_printf(f, "%5d: %08X:%04X %08X:%04X" - " %02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p %d%n", + " %02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %pK %d%n", bucket, src, srcp, dest, destp, sp->sk_state, sk_wmem_alloc_get(sp), sk_rmem_alloc_get(sp), diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c index 571aa96a175..2d51840e53a 100644 --- a/net/ipv4/xfrm4_output.c +++ b/net/ipv4/xfrm4_output.c @@ -69,7 +69,7 @@ int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb) } EXPORT_SYMBOL(xfrm4_prepare_output); -static int xfrm4_output_finish(struct sk_buff *skb) +int xfrm4_output_finish(struct sk_buff *skb) { #ifdef CONFIG_NETFILTER if (!skb_dst(skb)->xfrm) { @@ -86,7 +86,11 @@ static int xfrm4_output_finish(struct sk_buff *skb) int xfrm4_output(struct sk_buff *skb) { + struct dst_entry *dst = skb_dst(skb); + struct xfrm_state *x = dst->xfrm; + return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, skb, - NULL, skb_dst(skb)->dev, xfrm4_output_finish, + NULL, dst->dev, + x->outer_mode->afinfo->output_finish, !(IPCB(skb)->flags & IPSKB_REROUTED)); } diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index d20a05e970d..981e43eaf70 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -18,38 +18,46 @@ static struct xfrm_policy_afinfo xfrm4_policy_afinfo; -static struct dst_entry *xfrm4_dst_lookup(struct net *net, int tos, - const xfrm_address_t *saddr, - const xfrm_address_t *daddr) +static struct dst_entry *__xfrm4_dst_lookup(struct net *net, struct flowi4 *fl4, + int tos, + const xfrm_address_t *saddr, + const xfrm_address_t *daddr) { - struct flowi4 fl4 = { - .daddr = daddr->a4, - .flowi4_tos = tos, - }; struct rtable *rt; + memset(fl4, 0, sizeof(*fl4)); + fl4->daddr = daddr->a4; + fl4->flowi4_tos = tos; if (saddr) - fl4.saddr = saddr->a4; + fl4->saddr = saddr->a4; - rt = __ip_route_output_key(net, &fl4); + rt = __ip_route_output_key(net, fl4); if (!IS_ERR(rt)) return &rt->dst; return ERR_CAST(rt); } +static struct dst_entry *xfrm4_dst_lookup(struct net *net, int tos, + const xfrm_address_t *saddr, + const xfrm_address_t *daddr) +{ + struct flowi4 fl4; + + return __xfrm4_dst_lookup(net, &fl4, tos, saddr, daddr); +} + static int xfrm4_get_saddr(struct net *net, xfrm_address_t *saddr, xfrm_address_t *daddr) { struct dst_entry *dst; - struct rtable *rt; + struct flowi4 fl4; - dst = xfrm4_dst_lookup(net, 0, NULL, daddr); + dst = __xfrm4_dst_lookup(net, &fl4, 0, NULL, daddr); if (IS_ERR(dst)) return -EHOSTUNREACH; - rt = (struct rtable *)dst; - saddr->a4 = rt->rt_src; + saddr->a4 = fl4.saddr; dst_release(dst); return 0; } @@ -73,7 +81,7 @@ static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, rt->rt_key_dst = fl4->daddr; rt->rt_key_src = fl4->saddr; - rt->rt_tos = fl4->flowi4_tos; + rt->rt_key_tos = fl4->flowi4_tos; rt->rt_route_iif = fl4->flowi4_iif; rt->rt_iif = fl4->flowi4_iif; rt->rt_oif = fl4->flowi4_oif; @@ -102,7 +110,7 @@ static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, static void _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse) { - struct iphdr *iph = ip_hdr(skb); + const struct iphdr *iph = ip_hdr(skb); u8 *xprth = skb_network_header(skb) + iph->ihl * 4; struct flowi4 *fl4 = &fl->u.ip4; diff --git a/net/ipv4/xfrm4_state.c b/net/ipv4/xfrm4_state.c index 1717c64628d..d9ac0a0058b 100644 --- a/net/ipv4/xfrm4_state.c +++ b/net/ipv4/xfrm4_state.c @@ -55,7 +55,7 @@ xfrm4_init_temprop(struct xfrm_state *x, const struct xfrm_tmpl *tmpl, int xfrm4_extract_header(struct sk_buff *skb) { - struct iphdr *iph = ip_hdr(skb); + const struct iphdr *iph = ip_hdr(skb); XFRM_MODE_SKB_CB(skb)->ihl = sizeof(*iph); XFRM_MODE_SKB_CB(skb)->id = iph->id; @@ -78,6 +78,7 @@ static struct xfrm_state_afinfo xfrm4_state_afinfo = { .init_tempsel = __xfrm4_init_tempsel, .init_temprop = xfrm4_init_temprop, .output = xfrm4_output, + .output_finish = xfrm4_output_finish, .extract_input = xfrm4_extract_input, .extract_output = xfrm4_extract_output, .transport_finish = xfrm4_transport_finish, diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 129d7e1f311..05838c7fcf6 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -289,19 +289,19 @@ static int snmp6_alloc_dev(struct inet6_dev *idev) sizeof(struct ipstats_mib), __alignof__(struct ipstats_mib)) < 0) goto err_ip; - if (snmp_mib_init((void __percpu **)idev->stats.icmpv6, - sizeof(struct icmpv6_mib), - __alignof__(struct icmpv6_mib)) < 0) + idev->stats.icmpv6dev = kzalloc(sizeof(struct icmpv6_mib_device), + GFP_KERNEL); + if (!idev->stats.icmpv6dev) goto err_icmp; - if (snmp_mib_init((void __percpu **)idev->stats.icmpv6msg, - sizeof(struct icmpv6msg_mib), - __alignof__(struct icmpv6msg_mib)) < 0) + idev->stats.icmpv6msgdev = kzalloc(sizeof(struct icmpv6msg_mib_device), + GFP_KERNEL); + if (!idev->stats.icmpv6msgdev) goto err_icmpmsg; return 0; err_icmpmsg: - snmp_mib_free((void __percpu **)idev->stats.icmpv6); + kfree(idev->stats.icmpv6dev); err_icmp: snmp_mib_free((void __percpu **)idev->stats.ipv6); err_ip: @@ -310,19 +310,13 @@ err_ip: static void snmp6_free_dev(struct inet6_dev *idev) { - snmp_mib_free((void __percpu **)idev->stats.icmpv6msg); - snmp_mib_free((void __percpu **)idev->stats.icmpv6); + kfree(idev->stats.icmpv6msgdev); + kfree(idev->stats.icmpv6dev); snmp_mib_free((void __percpu **)idev->stats.ipv6); } /* Nobody refers to this device, we may destroy it. */ -static void in6_dev_finish_destroy_rcu(struct rcu_head *head) -{ - struct inet6_dev *idev = container_of(head, struct inet6_dev, rcu); - kfree(idev); -} - void in6_dev_finish_destroy(struct inet6_dev *idev) { struct net_device *dev = idev->dev; @@ -339,7 +333,7 @@ void in6_dev_finish_destroy(struct inet6_dev *idev) return; } snmp6_free_dev(idev); - call_rcu(&idev->rcu, in6_dev_finish_destroy_rcu); + kfree_rcu(idev, rcu); } EXPORT_SYMBOL(in6_dev_finish_destroy); @@ -535,12 +529,6 @@ static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int old) } #endif -static void inet6_ifa_finish_destroy_rcu(struct rcu_head *head) -{ - struct inet6_ifaddr *ifp = container_of(head, struct inet6_ifaddr, rcu); - kfree(ifp); -} - /* Nobody refers to this ifaddr, destroy it */ void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp) { @@ -561,7 +549,7 @@ void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp) } dst_release(&ifp->rt->dst); - call_rcu(&ifp->rcu, inet6_ifa_finish_destroy_rcu); + kfree_rcu(ifp, rcu); } static void @@ -1283,7 +1271,7 @@ static int ipv6_count_addresses(struct inet6_dev *idev) return cnt; } -int ipv6_chk_addr(struct net *net, struct in6_addr *addr, +int ipv6_chk_addr(struct net *net, const struct in6_addr *addr, struct net_device *dev, int strict) { struct inet6_ifaddr *ifp; @@ -1326,7 +1314,7 @@ static bool ipv6_chk_same_addr(struct net *net, const struct in6_addr *addr, return false; } -int ipv6_chk_prefix(struct in6_addr *addr, struct net_device *dev) +int ipv6_chk_prefix(const struct in6_addr *addr, struct net_device *dev) { struct inet6_dev *idev; struct inet6_ifaddr *ifa; @@ -1457,7 +1445,7 @@ void addrconf_dad_failure(struct inet6_ifaddr *ifp) /* Join to solicited addr multicast group. */ -void addrconf_join_solict(struct net_device *dev, struct in6_addr *addr) +void addrconf_join_solict(struct net_device *dev, const struct in6_addr *addr) { struct in6_addr maddr; @@ -1468,7 +1456,7 @@ void addrconf_join_solict(struct net_device *dev, struct in6_addr *addr) ipv6_dev_mc_inc(dev, &maddr); } -void addrconf_leave_solict(struct inet6_dev *idev, struct in6_addr *addr) +void addrconf_leave_solict(struct inet6_dev *idev, const struct in6_addr *addr) { struct in6_addr maddr; @@ -1571,6 +1559,11 @@ static int addrconf_ifid_sit(u8 *eui, struct net_device *dev) return -1; } +static int addrconf_ifid_gre(u8 *eui, struct net_device *dev) +{ + return __ipv6_isatap_ifid(eui, *(__be32 *)dev->dev_addr); +} + static int ipv6_generate_eui64(u8 *eui, struct net_device *dev) { switch (dev->type) { @@ -1584,6 +1577,8 @@ static int ipv6_generate_eui64(u8 *eui, struct net_device *dev) return addrconf_ifid_infiniband(eui, dev); case ARPHRD_SIT: return addrconf_ifid_sit(eui, dev); + case ARPHRD_IPGRE: + return addrconf_ifid_gre(eui, dev); } return -1; } @@ -2113,7 +2108,7 @@ err_exit: /* * Manual configuration of address on an interface */ -static int inet6_addr_add(struct net *net, int ifindex, struct in6_addr *pfx, +static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *pfx, unsigned int plen, __u8 ifa_flags, __u32 prefered_lft, __u32 valid_lft) { @@ -2187,7 +2182,7 @@ static int inet6_addr_add(struct net *net, int ifindex, struct in6_addr *pfx, return PTR_ERR(ifp); } -static int inet6_addr_del(struct net *net, int ifindex, struct in6_addr *pfx, +static int inet6_addr_del(struct net *net, int ifindex, const struct in6_addr *pfx, unsigned int plen) { struct inet6_ifaddr *ifp; @@ -2350,7 +2345,7 @@ static void init_loopback(struct net_device *dev) add_addr(idev, &in6addr_loopback, 128, IFA_HOST); } -static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr) +static void addrconf_add_linklocal(struct inet6_dev *idev, const struct in6_addr *addr) { struct inet6_ifaddr * ifp; u32 addr_flags = IFA_F_PERMANENT; @@ -2435,6 +2430,29 @@ static void addrconf_sit_config(struct net_device *dev) } #endif +#if defined(CONFIG_NET_IPGRE) || defined(CONFIG_NET_IPGRE_MODULE) +static void addrconf_gre_config(struct net_device *dev) +{ + struct inet6_dev *idev; + struct in6_addr addr; + + pr_info("ipv6: addrconf_gre_config(%s)\n", dev->name); + + ASSERT_RTNL(); + + if ((idev = ipv6_find_idev(dev)) == NULL) { + printk(KERN_DEBUG "init gre: add_dev failed\n"); + return; + } + + ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0); + addrconf_prefix_route(&addr, 64, dev, 0, 0); + + if (!ipv6_generate_eui64(addr.s6_addr + 8, dev)) + addrconf_add_linklocal(idev, &addr); +} +#endif + static inline int ipv6_inherit_linklocal(struct inet6_dev *idev, struct net_device *link_dev) { @@ -2551,6 +2569,11 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event, addrconf_sit_config(dev); break; #endif +#if defined(CONFIG_NET_IPGRE) || defined(CONFIG_NET_IPGRE_MODULE) + case ARPHRD_IPGRE: + addrconf_gre_config(dev); + break; +#endif case ARPHRD_TUNNEL6: addrconf_ip6_tnl_config(dev); break; @@ -3121,7 +3144,7 @@ void if6_proc_exit(void) #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) /* Check if address is a home address configured on any interface. */ -int ipv6_chk_home_addr(struct net *net, struct in6_addr *addr) +int ipv6_chk_home_addr(struct net *net, const struct in6_addr *addr) { int ret = 0; struct inet6_ifaddr *ifp = NULL; @@ -3838,7 +3861,7 @@ static inline size_t inet6_if_nlmsg_size(void) + nla_total_size(inet6_ifla6_size()); /* IFLA_PROTINFO */ } -static inline void __snmp6_fill_stats(u64 *stats, void __percpu **mib, +static inline void __snmp6_fill_statsdev(u64 *stats, atomic_long_t *mib, int items, int bytes) { int i; @@ -3848,7 +3871,7 @@ static inline void __snmp6_fill_stats(u64 *stats, void __percpu **mib, /* Use put_unaligned() because stats may not be aligned for u64. */ put_unaligned(items, &stats[0]); for (i = 1; i < items; i++) - put_unaligned(snmp_fold_field(mib, i), &stats[i]); + put_unaligned(atomic_long_read(&mib[i]), &stats[i]); memset(&stats[items], 0, pad); } @@ -3877,7 +3900,7 @@ static void snmp6_fill_stats(u64 *stats, struct inet6_dev *idev, int attrtype, IPSTATS_MIB_MAX, bytes, offsetof(struct ipstats_mib, syncp)); break; case IFLA_INET6_ICMP6STATS: - __snmp6_fill_stats(stats, (void __percpu **)idev->stats.icmpv6, ICMP6_MIB_MAX, bytes); + __snmp6_fill_statsdev(stats, idev->stats.icmpv6dev->mibs, ICMP6_MIB_MAX, bytes); break; } } @@ -4539,7 +4562,7 @@ static void __addrconf_sysctl_unregister(struct ipv6_devconf *p) t = p->sysctl; p->sysctl = NULL; - unregister_sysctl_table(t->sysctl_header); + unregister_net_sysctl_table(t->sysctl_header); kfree(t->dev_name); kfree(t); } @@ -4704,16 +4727,20 @@ int __init addrconf_init(void) if (err < 0) goto errout_af; - err = __rtnl_register(PF_INET6, RTM_GETLINK, NULL, inet6_dump_ifinfo); + err = __rtnl_register(PF_INET6, RTM_GETLINK, NULL, inet6_dump_ifinfo, + NULL); if (err < 0) goto errout; /* Only the first call to __rtnl_register can fail */ - __rtnl_register(PF_INET6, RTM_NEWADDR, inet6_rtm_newaddr, NULL); - __rtnl_register(PF_INET6, RTM_DELADDR, inet6_rtm_deladdr, NULL); - __rtnl_register(PF_INET6, RTM_GETADDR, inet6_rtm_getaddr, inet6_dump_ifaddr); - __rtnl_register(PF_INET6, RTM_GETMULTICAST, NULL, inet6_dump_ifmcaddr); - __rtnl_register(PF_INET6, RTM_GETANYCAST, NULL, inet6_dump_ifacaddr); + __rtnl_register(PF_INET6, RTM_NEWADDR, inet6_rtm_newaddr, NULL, NULL); + __rtnl_register(PF_INET6, RTM_DELADDR, inet6_rtm_deladdr, NULL, NULL); + __rtnl_register(PF_INET6, RTM_GETADDR, inet6_rtm_getaddr, + inet6_dump_ifaddr, NULL); + __rtnl_register(PF_INET6, RTM_GETMULTICAST, NULL, + inet6_dump_ifmcaddr, NULL); + __rtnl_register(PF_INET6, RTM_GETANYCAST, NULL, + inet6_dump_ifacaddr, NULL); ipv6_addr_label_rtnl_register(); diff --git a/net/ipv6/addrlabel.c b/net/ipv6/addrlabel.c index c8993e5a337..2d8ddba9ee5 100644 --- a/net/ipv6/addrlabel.c +++ b/net/ipv6/addrlabel.c @@ -592,8 +592,11 @@ out: void __init ipv6_addr_label_rtnl_register(void) { - __rtnl_register(PF_INET6, RTM_NEWADDRLABEL, ip6addrlbl_newdel, NULL); - __rtnl_register(PF_INET6, RTM_DELADDRLABEL, ip6addrlbl_newdel, NULL); - __rtnl_register(PF_INET6, RTM_GETADDRLABEL, ip6addrlbl_get, ip6addrlbl_dump); + __rtnl_register(PF_INET6, RTM_NEWADDRLABEL, ip6addrlbl_newdel, + NULL, NULL); + __rtnl_register(PF_INET6, RTM_DELADDRLABEL, ip6addrlbl_newdel, + NULL, NULL); + __rtnl_register(PF_INET6, RTM_GETADDRLABEL, ip6addrlbl_get, + ip6addrlbl_dump, NULL); } diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index afcc7099f96..b7919f901fb 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -740,7 +740,7 @@ static int ipv6_gso_pull_exthdrs(struct sk_buff *skb, int proto) static int ipv6_gso_send_check(struct sk_buff *skb) { - struct ipv6hdr *ipv6h; + const struct ipv6hdr *ipv6h; const struct inet6_protocol *ops; int err = -EINVAL; diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c index 0e5e943446f..674255f5e6b 100644 --- a/net/ipv6/anycast.c +++ b/net/ipv6/anycast.c @@ -44,7 +44,7 @@ #include <net/checksum.h> -static int ipv6_dev_ac_dec(struct net_device *dev, struct in6_addr *addr); +static int ipv6_dev_ac_dec(struct net_device *dev, const struct in6_addr *addr); /* Big ac list lock for all the sockets */ static DEFINE_RWLOCK(ipv6_sk_ac_lock); @@ -54,7 +54,7 @@ static DEFINE_RWLOCK(ipv6_sk_ac_lock); * socket join an anycast group */ -int ipv6_sock_ac_join(struct sock *sk, int ifindex, struct in6_addr *addr) +int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr) { struct ipv6_pinfo *np = inet6_sk(sk); struct net_device *dev = NULL; @@ -145,7 +145,7 @@ error: /* * socket leave an anycast group */ -int ipv6_sock_ac_drop(struct sock *sk, int ifindex, struct in6_addr *addr) +int ipv6_sock_ac_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) { struct ipv6_pinfo *np = inet6_sk(sk); struct net_device *dev; @@ -252,7 +252,7 @@ static void aca_put(struct ifacaddr6 *ac) /* * device anycast group inc (add if not found) */ -int ipv6_dev_ac_inc(struct net_device *dev, struct in6_addr *addr) +int ipv6_dev_ac_inc(struct net_device *dev, const struct in6_addr *addr) { struct ifacaddr6 *aca; struct inet6_dev *idev; @@ -324,7 +324,7 @@ out: /* * device anycast group decrement */ -int __ipv6_dev_ac_dec(struct inet6_dev *idev, struct in6_addr *addr) +int __ipv6_dev_ac_dec(struct inet6_dev *idev, const struct in6_addr *addr) { struct ifacaddr6 *aca, *prev_aca; @@ -358,7 +358,7 @@ int __ipv6_dev_ac_dec(struct inet6_dev *idev, struct in6_addr *addr) } /* called with rcu_read_lock() */ -static int ipv6_dev_ac_dec(struct net_device *dev, struct in6_addr *addr) +static int ipv6_dev_ac_dec(struct net_device *dev, const struct in6_addr *addr) { struct inet6_dev *idev = __in6_dev_get(dev); @@ -371,7 +371,7 @@ static int ipv6_dev_ac_dec(struct net_device *dev, struct in6_addr *addr) * check if the interface has this anycast address * called with rcu_read_lock() */ -static int ipv6_chk_acast_dev(struct net_device *dev, struct in6_addr *addr) +static int ipv6_chk_acast_dev(struct net_device *dev, const struct in6_addr *addr) { struct inet6_dev *idev; struct ifacaddr6 *aca; @@ -392,7 +392,7 @@ static int ipv6_chk_acast_dev(struct net_device *dev, struct in6_addr *addr) * check if given interface (or any, if dev==0) has this anycast address */ int ipv6_chk_acast_addr(struct net *net, struct net_device *dev, - struct in6_addr *addr) + const struct in6_addr *addr) { int found = 0; diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index 5aa8ec88f19..1ac7938dd9e 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -371,7 +371,7 @@ static int esp6_input(struct xfrm_state *x, struct sk_buff *skb) iv = esp_tmp_iv(aead, tmp, seqhilen); req = esp_tmp_req(aead, iv); asg = esp_req_sg(aead, req); - sg = asg + 1; + sg = asg + sglists; skb->ip_summed = CHECKSUM_NONE; @@ -430,7 +430,7 @@ static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, u8 type, u8 code, int offset, __be32 info) { struct net *net = dev_net(skb->dev); - struct ipv6hdr *iph = (struct ipv6hdr*)skb->data; + const struct ipv6hdr *iph = (const struct ipv6hdr *)skb->data; struct ip_esp_hdr *esph = (struct ip_esp_hdr *)(skb->data + offset); struct xfrm_state *x; @@ -438,7 +438,8 @@ static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, type != ICMPV6_PKT_TOOBIG) return; - x = xfrm_state_lookup(net, skb->mark, (xfrm_address_t *)&iph->daddr, esph->spi, IPPROTO_ESP, AF_INET6); + x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr, + esph->spi, IPPROTO_ESP, AF_INET6); if (!x) return; printk(KERN_DEBUG "pmtu discovery on SA ESP/%08x/%pI6\n", diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 83cb4f9add8..11900417b1c 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -372,7 +372,7 @@ void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) struct ipv6hdr *hdr = ipv6_hdr(skb); struct sock *sk; struct ipv6_pinfo *np; - struct in6_addr *saddr = NULL; + const struct in6_addr *saddr = NULL; struct dst_entry *dst; struct icmp6hdr tmp_hdr; struct flowi6 fl6; @@ -521,7 +521,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) struct sock *sk; struct inet6_dev *idev; struct ipv6_pinfo *np; - struct in6_addr *saddr = NULL; + const struct in6_addr *saddr = NULL; struct icmp6hdr *icmph = icmp6_hdr(skb); struct icmp6hdr tmp_hdr; struct flowi6 fl6; @@ -645,8 +645,8 @@ static int icmpv6_rcv(struct sk_buff *skb) { struct net_device *dev = skb->dev; struct inet6_dev *idev = __in6_dev_get(dev); - struct in6_addr *saddr, *daddr; - struct ipv6hdr *orig_hdr; + const struct in6_addr *saddr, *daddr; + const struct ipv6hdr *orig_hdr; struct icmp6hdr *hdr; u8 type; diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index f2c5b0fc0f2..8a58e8cf664 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c @@ -203,7 +203,7 @@ struct dst_entry *__inet6_csk_dst_check(struct sock *sk, u32 cookie) return dst; } -int inet6_csk_xmit(struct sk_buff *skb) +int inet6_csk_xmit(struct sk_buff *skb, struct flowi *fl_unused) { struct sock *sk = skb->sk; struct inet_sock *inet = inet_sk(sk); diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 7548905e79e..3030bdfd3ca 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -134,9 +134,9 @@ static __inline__ u32 fib6_new_sernum(void) # define BITOP_BE32_SWIZZLE 0 #endif -static __inline__ __be32 addr_bit_set(void *token, int fn_bit) +static __inline__ __be32 addr_bit_set(const void *token, int fn_bit) { - __be32 *addr = token; + const __be32 *addr = token; /* * Here, * 1 << ((~fn_bit ^ BITOP_BE32_SWIZZLE) & 0x1f) @@ -394,10 +394,11 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) arg.net = net; w->args = &arg; + rcu_read_lock(); for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_e = 0) { e = 0; head = &net->ipv6.fib_table_hash[h]; - hlist_for_each_entry(tb, node, head, tb6_hlist) { + hlist_for_each_entry_rcu(tb, node, head, tb6_hlist) { if (e < s_e) goto next; res = fib6_dump_table(tb, skb, cb); @@ -408,6 +409,7 @@ next: } } out: + rcu_read_unlock(); cb->args[1] = e; cb->args[0] = h; @@ -822,7 +824,7 @@ st_failure: struct lookup_args { int offset; /* key offset on rt6_info */ - struct in6_addr *addr; /* search key */ + const struct in6_addr *addr; /* search key */ }; static struct fib6_node * fib6_lookup_1(struct fib6_node *root, @@ -881,8 +883,8 @@ static struct fib6_node * fib6_lookup_1(struct fib6_node *root, return NULL; } -struct fib6_node * fib6_lookup(struct fib6_node *root, struct in6_addr *daddr, - struct in6_addr *saddr) +struct fib6_node * fib6_lookup(struct fib6_node *root, const struct in6_addr *daddr, + const struct in6_addr *saddr) { struct fib6_node *fn; struct lookup_args args[] = { @@ -916,7 +918,7 @@ struct fib6_node * fib6_lookup(struct fib6_node *root, struct in6_addr *daddr, static struct fib6_node * fib6_locate_1(struct fib6_node *root, - struct in6_addr *addr, + const struct in6_addr *addr, int plen, int offset) { struct fib6_node *fn; @@ -946,8 +948,8 @@ static struct fib6_node * fib6_locate_1(struct fib6_node *root, } struct fib6_node * fib6_locate(struct fib6_node *root, - struct in6_addr *daddr, int dst_len, - struct in6_addr *saddr, int src_len) + const struct in6_addr *daddr, int dst_len, + const struct in6_addr *saddr, int src_len) { struct fib6_node *fn; @@ -1584,7 +1586,8 @@ int __init fib6_init(void) if (ret) goto out_kmem_cache_create; - ret = __rtnl_register(PF_INET6, RTM_GETROUTE, NULL, inet6_dump_fib); + ret = __rtnl_register(PF_INET6, RTM_GETROUTE, NULL, inet6_dump_fib, + NULL); if (ret) goto out_unregister_subsys; out: diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index a83e9209cec..027c7ff6f1e 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -57,7 +57,7 @@ inline int ip6_rcv_finish( struct sk_buff *skb) int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { - struct ipv6hdr *hdr; + const struct ipv6hdr *hdr; u32 pkt_len; struct inet6_dev *idev; struct net *net = dev_net(skb->dev); @@ -186,7 +186,7 @@ resubmit: int ret; if (ipprot->flags & INET6_PROTO_FINAL) { - struct ipv6hdr *hdr; + const struct ipv6hdr *hdr; /* Free reference early: we don't need it any more, and it may hold ip_conntrack module loaded @@ -242,7 +242,7 @@ int ip6_input(struct sk_buff *skb) int ip6_mc_input(struct sk_buff *skb) { - struct ipv6hdr *hdr; + const struct ipv6hdr *hdr; int deliver; IP6_UPD_PO_STATS_BH(dev_net(skb_dst(skb)->dev), diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index c614d02bf42..9d4b165837d 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -869,9 +869,9 @@ fail: return err; } -static inline int ip6_rt_check(struct rt6key *rt_key, - struct in6_addr *fl_addr, - struct in6_addr *addr_cache) +static inline int ip6_rt_check(const struct rt6key *rt_key, + const struct in6_addr *fl_addr, + const struct in6_addr *addr_cache) { return (rt_key->plen != 128 || !ipv6_addr_equal(fl_addr, &rt_key->addr)) && (addr_cache == NULL || !ipv6_addr_equal(fl_addr, addr_cache)); @@ -879,7 +879,7 @@ static inline int ip6_rt_check(struct rt6key *rt_key, static struct dst_entry *ip6_sk_dst_check(struct sock *sk, struct dst_entry *dst, - struct flowi6 *fl6) + const struct flowi6 *fl6) { struct ipv6_pinfo *np = inet6_sk(sk); struct rt6_info *rt = (struct rt6_info *)dst; @@ -1150,6 +1150,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, { struct inet_sock *inet = inet_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk); + struct inet_cork *cork; struct sk_buff *skb; unsigned int maxfraglen, fragheaderlen; int exthdrlen; @@ -1163,6 +1164,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, if (flags&MSG_PROBE) return 0; + cork = &inet->cork.base; if (skb_queue_empty(&sk->sk_write_queue)) { /* * setup for corking @@ -1202,7 +1204,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, /* need source address above miyazawa*/ } dst_hold(&rt->dst); - inet->cork.dst = &rt->dst; + cork->dst = &rt->dst; inet->cork.fl.u.ip6 = *fl6; np->cork.hop_limit = hlimit; np->cork.tclass = tclass; @@ -1212,10 +1214,10 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, if (np->frag_size) mtu = np->frag_size; } - inet->cork.fragsize = mtu; + cork->fragsize = mtu; if (dst_allfrag(rt->dst.path)) - inet->cork.flags |= IPCORK_ALLFRAG; - inet->cork.length = 0; + cork->flags |= IPCORK_ALLFRAG; + cork->length = 0; sk->sk_sndmsg_page = NULL; sk->sk_sndmsg_off = 0; exthdrlen = rt->dst.header_len + (opt ? opt->opt_flen : 0) - @@ -1223,12 +1225,12 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, length += exthdrlen; transhdrlen += exthdrlen; } else { - rt = (struct rt6_info *)inet->cork.dst; + rt = (struct rt6_info *)cork->dst; fl6 = &inet->cork.fl.u.ip6; opt = np->cork.opt; transhdrlen = 0; exthdrlen = 0; - mtu = inet->cork.fragsize; + mtu = cork->fragsize; } hh_len = LL_RESERVED_SPACE(rt->dst.dev); @@ -1238,7 +1240,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen - sizeof(struct frag_hdr); if (mtu <= sizeof(struct ipv6hdr) + IPV6_MAXPLEN) { - if (inet->cork.length + length > sizeof(struct ipv6hdr) + IPV6_MAXPLEN - fragheaderlen) { + if (cork->length + length > sizeof(struct ipv6hdr) + IPV6_MAXPLEN - fragheaderlen) { ipv6_local_error(sk, EMSGSIZE, fl6, mtu-exthdrlen); return -EMSGSIZE; } @@ -1267,7 +1269,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, * --yoshfuji */ - inet->cork.length += length; + cork->length += length; if (length > mtu) { int proto = sk->sk_protocol; if (dontfrag && (proto == IPPROTO_UDP || proto == IPPROTO_RAW)){ @@ -1292,7 +1294,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, while (length > 0) { /* Check if the remaining data fits into current packet. */ - copy = (inet->cork.length <= mtu && !(inet->cork.flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - skb->len; + copy = (cork->length <= mtu && !(cork->flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - skb->len; if (copy < length) copy = maxfraglen - skb->len; @@ -1317,7 +1319,7 @@ alloc_new_skb: * we know we need more fragment(s). */ datalen = length + fraggap; - if (datalen > (inet->cork.length <= mtu && !(inet->cork.flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - fragheaderlen) + if (datalen > (cork->length <= mtu && !(cork->flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - fragheaderlen) datalen = maxfraglen - fragheaderlen; fraglen = datalen + fragheaderlen; @@ -1481,7 +1483,7 @@ alloc_new_skb: } return 0; error: - inet->cork.length -= length; + cork->length -= length; IP6_INC_STATS(sock_net(sk), rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS); return err; } @@ -1497,10 +1499,10 @@ static void ip6_cork_release(struct inet_sock *inet, struct ipv6_pinfo *np) np->cork.opt = NULL; } - if (inet->cork.dst) { - dst_release(inet->cork.dst); - inet->cork.dst = NULL; - inet->cork.flags &= ~IPCORK_ALLFRAG; + if (inet->cork.base.dst) { + dst_release(inet->cork.base.dst); + inet->cork.base.dst = NULL; + inet->cork.base.flags &= ~IPCORK_ALLFRAG; } memset(&inet->cork.fl, 0, sizeof(inet->cork.fl)); } @@ -1515,7 +1517,7 @@ int ip6_push_pending_frames(struct sock *sk) struct net *net = sock_net(sk); struct ipv6hdr *hdr; struct ipv6_txoptions *opt = np->cork.opt; - struct rt6_info *rt = (struct rt6_info *)inet->cork.dst; + struct rt6_info *rt = (struct rt6_info *)inet->cork.base.dst; struct flowi6 *fl6 = &inet->cork.fl.u.ip6; unsigned char proto = fl6->flowi6_proto; int err = 0; diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index c1b1bd312df..36c2842a86b 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -162,7 +162,7 @@ static inline void ip6_tnl_dst_store(struct ip6_tnl *t, struct dst_entry *dst) for (t = rcu_dereference(start); t; t = rcu_dereference(t->next)) static struct ip6_tnl * -ip6_tnl_lookup(struct net *net, struct in6_addr *remote, struct in6_addr *local) +ip6_tnl_lookup(struct net *net, const struct in6_addr *remote, const struct in6_addr *local) { unsigned int h0 = HASH(remote); unsigned int h1 = HASH(local); @@ -194,10 +194,10 @@ ip6_tnl_lookup(struct net *net, struct in6_addr *remote, struct in6_addr *local) **/ static struct ip6_tnl __rcu ** -ip6_tnl_bucket(struct ip6_tnl_net *ip6n, struct ip6_tnl_parm *p) +ip6_tnl_bucket(struct ip6_tnl_net *ip6n, const struct ip6_tnl_parm *p) { - struct in6_addr *remote = &p->raddr; - struct in6_addr *local = &p->laddr; + const struct in6_addr *remote = &p->raddr; + const struct in6_addr *local = &p->laddr; unsigned h = 0; int prio = 0; @@ -280,11 +280,6 @@ static struct ip6_tnl *ip6_tnl_create(struct net *net, struct ip6_tnl_parm *p) dev_net_set(dev, net); - if (strchr(name, '%')) { - if (dev_alloc_name(dev, name) < 0) - goto failed_free; - } - t = netdev_priv(dev); t->parms = *p; err = ip6_tnl_dev_init(dev); @@ -321,8 +316,8 @@ failed: static struct ip6_tnl *ip6_tnl_locate(struct net *net, struct ip6_tnl_parm *p, int create) { - struct in6_addr *remote = &p->raddr; - struct in6_addr *local = &p->laddr; + const struct in6_addr *remote = &p->raddr; + const struct in6_addr *local = &p->laddr; struct ip6_tnl __rcu **tp; struct ip6_tnl *t; struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); @@ -374,7 +369,7 @@ ip6_tnl_dev_uninit(struct net_device *dev) static __u16 parse_tlv_tnl_enc_lim(struct sk_buff *skb, __u8 * raw) { - struct ipv6hdr *ipv6h = (struct ipv6hdr *) raw; + const struct ipv6hdr *ipv6h = (const struct ipv6hdr *) raw; __u8 nexthdr = ipv6h->nexthdr; __u16 off = sizeof (*ipv6h); @@ -435,7 +430,7 @@ static int ip6_tnl_err(struct sk_buff *skb, __u8 ipproto, struct inet6_skb_parm *opt, u8 *type, u8 *code, int *msg, __u32 *info, int offset) { - struct ipv6hdr *ipv6h = (struct ipv6hdr *) skb->data; + const struct ipv6hdr *ipv6h = (const struct ipv6hdr *) skb->data; struct ip6_tnl *t; int rel_msg = 0; u8 rel_type = ICMPV6_DEST_UNREACH; @@ -535,8 +530,9 @@ ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, __u32 rel_info = ntohl(info); int err; struct sk_buff *skb2; - struct iphdr *eiph; + const struct iphdr *eiph; struct rtable *rt; + struct flowi4 fl4; err = ip6_tnl_err(skb, IPPROTO_IPIP, opt, &rel_type, &rel_code, &rel_msg, &rel_info, offset); @@ -577,7 +573,7 @@ ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, eiph = ip_hdr(skb2); /* Try to guess incoming interface */ - rt = ip_route_output_ports(dev_net(skb->dev), NULL, + rt = ip_route_output_ports(dev_net(skb->dev), &fl4, NULL, eiph->saddr, 0, 0, 0, IPPROTO_IPIP, RT_TOS(eiph->tos), 0); @@ -590,7 +586,7 @@ ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (rt->rt_flags & RTCF_LOCAL) { ip_rt_put(rt); rt = NULL; - rt = ip_route_output_ports(dev_net(skb->dev), NULL, + rt = ip_route_output_ports(dev_net(skb->dev), &fl4, NULL, eiph->daddr, eiph->saddr, 0, 0, IPPROTO_IPIP, @@ -669,8 +665,8 @@ ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, return 0; } -static void ip4ip6_dscp_ecn_decapsulate(struct ip6_tnl *t, - struct ipv6hdr *ipv6h, +static void ip4ip6_dscp_ecn_decapsulate(const struct ip6_tnl *t, + const struct ipv6hdr *ipv6h, struct sk_buff *skb) { __u8 dsfield = ipv6_get_dsfield(ipv6h) & ~INET_ECN_MASK; @@ -682,8 +678,8 @@ static void ip4ip6_dscp_ecn_decapsulate(struct ip6_tnl *t, IP_ECN_set_ce(ip_hdr(skb)); } -static void ip6ip6_dscp_ecn_decapsulate(struct ip6_tnl *t, - struct ipv6hdr *ipv6h, +static void ip6ip6_dscp_ecn_decapsulate(const struct ip6_tnl *t, + const struct ipv6hdr *ipv6h, struct sk_buff *skb) { if (t->parms.flags & IP6_TNL_F_RCV_DSCP_COPY) @@ -726,12 +722,12 @@ static inline int ip6_tnl_rcv_ctl(struct ip6_tnl *t) static int ip6_tnl_rcv(struct sk_buff *skb, __u16 protocol, __u8 ipproto, - void (*dscp_ecn_decapsulate)(struct ip6_tnl *t, - struct ipv6hdr *ipv6h, + void (*dscp_ecn_decapsulate)(const struct ip6_tnl *t, + const struct ipv6hdr *ipv6h, struct sk_buff *skb)) { struct ip6_tnl *t; - struct ipv6hdr *ipv6h = ipv6_hdr(skb); + const struct ipv6hdr *ipv6h = ipv6_hdr(skb); rcu_read_lock(); @@ -828,7 +824,7 @@ static void init_tel_txopt(struct ipv6_tel_txoption *opt, __u8 encap_limit) **/ static inline int -ip6_tnl_addr_conflict(struct ip6_tnl *t, struct ipv6hdr *hdr) +ip6_tnl_addr_conflict(const struct ip6_tnl *t, const struct ipv6hdr *hdr) { return ipv6_addr_equal(&t->parms.raddr, &hdr->saddr); } @@ -1005,7 +1001,7 @@ static inline int ip4ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) { struct ip6_tnl *t = netdev_priv(dev); - struct iphdr *iph = ip_hdr(skb); + const struct iphdr *iph = ip_hdr(skb); int encap_limit = -1; struct flowi6 fl6; __u8 dsfield; diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 29e48593bf2..705c8288628 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -989,8 +989,8 @@ static int mif6_add(struct net *net, struct mr6_table *mrt, } static struct mfc6_cache *ip6mr_cache_find(struct mr6_table *mrt, - struct in6_addr *origin, - struct in6_addr *mcastgrp) + const struct in6_addr *origin, + const struct in6_addr *mcastgrp) { int line = MFC6_HASH(mcastgrp, origin); struct mfc6_cache *c; @@ -1354,7 +1354,8 @@ int __init ip6_mr_init(void) goto add_proto_fail; } #endif - rtnl_register(RTNL_FAMILY_IP6MR, RTM_GETROUTE, NULL, ip6mr_rtm_dumproute); + rtnl_register(RTNL_FAMILY_IP6MR, RTM_GETROUTE, NULL, + ip6mr_rtm_dumproute, NULL); return 0; #ifdef CONFIG_IPV6_PIMSM_V2 add_proto_fail: diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c index 85cccd6ed0b..bba658d9a03 100644 --- a/net/ipv6/ipcomp6.c +++ b/net/ipv6/ipcomp6.c @@ -55,7 +55,7 @@ static void ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, { struct net *net = dev_net(skb->dev); __be32 spi; - struct ipv6hdr *iph = (struct ipv6hdr*)skb->data; + const struct ipv6hdr *iph = (const struct ipv6hdr *)skb->data; struct ip_comp_hdr *ipcomph = (struct ip_comp_hdr *)(skb->data + offset); struct xfrm_state *x; @@ -64,7 +64,8 @@ static void ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, return; spi = htonl(ntohs(ipcomph->cpi)); - x = xfrm_state_lookup(net, skb->mark, (xfrm_address_t *)&iph->daddr, spi, IPPROTO_COMP, AF_INET6); + x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr, + spi, IPPROTO_COMP, AF_INET6); if (!x) return; diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 76b893771e6..3e6ebcdb477 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -92,16 +92,16 @@ static void mld_gq_timer_expire(unsigned long data); static void mld_ifc_timer_expire(unsigned long data); static void mld_ifc_event(struct inet6_dev *idev); static void mld_add_delrec(struct inet6_dev *idev, struct ifmcaddr6 *pmc); -static void mld_del_delrec(struct inet6_dev *idev, struct in6_addr *addr); +static void mld_del_delrec(struct inet6_dev *idev, const struct in6_addr *addr); static void mld_clear_delrec(struct inet6_dev *idev); static int sf_setstate(struct ifmcaddr6 *pmc); static void sf_markstate(struct ifmcaddr6 *pmc); static void ip6_mc_clear_src(struct ifmcaddr6 *pmc); -static int ip6_mc_del_src(struct inet6_dev *idev, struct in6_addr *pmca, - int sfmode, int sfcount, struct in6_addr *psfsrc, +static int ip6_mc_del_src(struct inet6_dev *idev, const struct in6_addr *pmca, + int sfmode, int sfcount, const struct in6_addr *psfsrc, int delta); -static int ip6_mc_add_src(struct inet6_dev *idev, struct in6_addr *pmca, - int sfmode, int sfcount, struct in6_addr *psfsrc, +static int ip6_mc_add_src(struct inet6_dev *idev, const struct in6_addr *pmca, + int sfmode, int sfcount, const struct in6_addr *psfsrc, int delta); static int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml, struct inet6_dev *idev); @@ -201,10 +201,6 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) return 0; } -static void ipv6_mc_socklist_reclaim(struct rcu_head *head) -{ - kfree(container_of(head, struct ipv6_mc_socklist, rcu)); -} /* * socket leave on multicast group */ @@ -239,7 +235,7 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) (void) ip6_mc_leave_src(sk, mc_lst, NULL); rcu_read_unlock(); atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc); - call_rcu(&mc_lst->rcu, ipv6_mc_socklist_reclaim); + kfree_rcu(mc_lst, rcu); return 0; } } @@ -250,7 +246,7 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) /* called with rcu_read_lock() */ static struct inet6_dev *ip6_mc_find_dev_rcu(struct net *net, - struct in6_addr *group, + const struct in6_addr *group, int ifindex) { struct net_device *dev = NULL; @@ -307,7 +303,7 @@ void ipv6_sock_mc_close(struct sock *sk) rcu_read_unlock(); atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc); - call_rcu(&mc_lst->rcu, ipv6_mc_socklist_reclaim); + kfree_rcu(mc_lst, rcu); spin_lock(&ipv6_sk_mc_lock); } @@ -451,7 +447,7 @@ done: int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf) { - struct in6_addr *group; + const struct in6_addr *group; struct ipv6_mc_socklist *pmc; struct inet6_dev *idev; struct ipv6_pinfo *inet6 = inet6_sk(sk); @@ -542,7 +538,7 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf, struct group_filter __user *optval, int __user *optlen) { int err, i, count, copycount; - struct in6_addr *group; + const struct in6_addr *group; struct ipv6_mc_socklist *pmc; struct inet6_dev *idev; struct ipv6_pinfo *inet6 = inet6_sk(sk); @@ -752,7 +748,7 @@ static void mld_add_delrec(struct inet6_dev *idev, struct ifmcaddr6 *im) spin_unlock_bh(&idev->mc_lock); } -static void mld_del_delrec(struct inet6_dev *idev, struct in6_addr *pmca) +static void mld_del_delrec(struct inet6_dev *idev, const struct in6_addr *pmca) { struct ifmcaddr6 *pmc, *pmc_prev; struct ip6_sf_list *psf, *psf_next; @@ -1052,7 +1048,7 @@ static void igmp6_group_queried(struct ifmcaddr6 *ma, unsigned long resptime) /* mark EXCLUDE-mode sources */ static int mld_xmarksources(struct ifmcaddr6 *pmc, int nsrcs, - struct in6_addr *srcs) + const struct in6_addr *srcs) { struct ip6_sf_list *psf; int i, scount; @@ -1080,7 +1076,7 @@ static int mld_xmarksources(struct ifmcaddr6 *pmc, int nsrcs, } static int mld_marksources(struct ifmcaddr6 *pmc, int nsrcs, - struct in6_addr *srcs) + const struct in6_addr *srcs) { struct ip6_sf_list *psf; int i, scount; @@ -1115,7 +1111,7 @@ int igmp6_event_query(struct sk_buff *skb) { struct mld2_query *mlh2 = NULL; struct ifmcaddr6 *ma; - struct in6_addr *group; + const struct in6_addr *group; unsigned long max_delay; struct inet6_dev *idev; struct mld_msg *mld; @@ -1821,7 +1817,7 @@ err_out: } static int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode, - struct in6_addr *psfsrc) + const struct in6_addr *psfsrc) { struct ip6_sf_list *psf, *psf_prev; int rv = 0; @@ -1857,8 +1853,8 @@ static int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode, return rv; } -static int ip6_mc_del_src(struct inet6_dev *idev, struct in6_addr *pmca, - int sfmode, int sfcount, struct in6_addr *psfsrc, +static int ip6_mc_del_src(struct inet6_dev *idev, const struct in6_addr *pmca, + int sfmode, int sfcount, const struct in6_addr *psfsrc, int delta) { struct ifmcaddr6 *pmc; @@ -1918,7 +1914,7 @@ static int ip6_mc_del_src(struct inet6_dev *idev, struct in6_addr *pmca, * Add multicast single-source filter to the interface list */ static int ip6_mc_add1_src(struct ifmcaddr6 *pmc, int sfmode, - struct in6_addr *psfsrc, int delta) + const struct in6_addr *psfsrc, int delta) { struct ip6_sf_list *psf, *psf_prev; @@ -2021,8 +2017,8 @@ static int sf_setstate(struct ifmcaddr6 *pmc) /* * Add multicast source filter list to the interface list */ -static int ip6_mc_add_src(struct inet6_dev *idev, struct in6_addr *pmca, - int sfmode, int sfcount, struct in6_addr *psfsrc, +static int ip6_mc_add_src(struct inet6_dev *idev, const struct in6_addr *pmca, + int sfmode, int sfcount, const struct in6_addr *psfsrc, int delta) { struct ifmcaddr6 *pmc; diff --git a/net/ipv6/mip6.c b/net/ipv6/mip6.c index 9b210482fb0..43242e6e610 100644 --- a/net/ipv6/mip6.c +++ b/net/ipv6/mip6.c @@ -126,7 +126,7 @@ static struct mip6_report_rate_limiter mip6_report_rl = { static int mip6_destopt_input(struct xfrm_state *x, struct sk_buff *skb) { - struct ipv6hdr *iph = ipv6_hdr(skb); + const struct ipv6hdr *iph = ipv6_hdr(skb); struct ipv6_destopt_hdr *destopt = (struct ipv6_destopt_hdr *)skb->data; int err = destopt->nexthdr; @@ -181,8 +181,8 @@ static int mip6_destopt_output(struct xfrm_state *x, struct sk_buff *skb) } static inline int mip6_report_rl_allow(struct timeval *stamp, - struct in6_addr *dst, - struct in6_addr *src, int iif) + const struct in6_addr *dst, + const struct in6_addr *src, int iif) { int allow = 0; @@ -349,7 +349,7 @@ static const struct xfrm_type mip6_destopt_type = static int mip6_rthdr_input(struct xfrm_state *x, struct sk_buff *skb) { - struct ipv6hdr *iph = ipv6_hdr(skb); + const struct ipv6hdr *iph = ipv6_hdr(skb); struct rt2_hdr *rt2 = (struct rt2_hdr *)skb->data; int err = rt2->rt_hdr.nexthdr; diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 01a0ffc7b40..7596f071d30 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -324,7 +324,7 @@ static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p, return lladdr + prepad; } -int ndisc_mc_map(struct in6_addr *addr, char *buf, struct net_device *dev, int dir) +int ndisc_mc_map(const struct in6_addr *addr, char *buf, struct net_device *dev, int dir) { switch (dev->type) { case ARPHRD_ETHER: @@ -748,8 +748,8 @@ static int pndisc_is_router(const void *pkey, static void ndisc_recv_ns(struct sk_buff *skb) { struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb); - struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; - struct in6_addr *daddr = &ipv6_hdr(skb)->daddr; + const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; + const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr; u8 *lladdr = NULL; u32 ndoptlen = skb->tail - (skb->transport_header + offsetof(struct nd_msg, opt)); @@ -924,8 +924,8 @@ out: static void ndisc_recv_na(struct sk_buff *skb) { struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb); - struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; - struct in6_addr *daddr = &ipv6_hdr(skb)->daddr; + const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; + const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr; u8 *lladdr = NULL; u32 ndoptlen = skb->tail - (skb->transport_header + offsetof(struct nd_msg, opt)); @@ -1038,7 +1038,7 @@ static void ndisc_recv_rs(struct sk_buff *skb) unsigned long ndoptlen = skb->len - sizeof(*rs_msg); struct neighbour *neigh; struct inet6_dev *idev; - struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; + const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; struct ndisc_options ndopts; u8 *lladdr = NULL; @@ -1435,8 +1435,8 @@ static void ndisc_redirect_rcv(struct sk_buff *skb) { struct inet6_dev *in6_dev; struct icmp6hdr *icmph; - struct in6_addr *dest; - struct in6_addr *target; /* new first hop to destination */ + const struct in6_addr *dest; + const struct in6_addr *target; /* new first hop to destination */ struct neighbour *neigh; int on_link = 0; struct ndisc_options ndopts; @@ -1469,7 +1469,7 @@ static void ndisc_redirect_rcv(struct sk_buff *skb) } icmph = icmp6_hdr(skb); - target = (struct in6_addr *) (icmph + 1); + target = (const struct in6_addr *) (icmph + 1); dest = target + 1; if (ipv6_addr_is_multicast(dest)) { @@ -1747,7 +1747,6 @@ static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, fib6_run_gc(~0UL, net); break; case NETDEV_NOTIFY_PEERS: - case NETDEV_BONDING_FAILOVER: ndisc_send_unsol_na(dev); break; default: diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c index 28bc1f644b7..30fcee46544 100644 --- a/net/ipv6/netfilter.c +++ b/net/ipv6/netfilter.c @@ -13,7 +13,7 @@ int ip6_route_me_harder(struct sk_buff *skb) { struct net *net = dev_net(skb_dst(skb)->dev); - struct ipv6hdr *iph = ipv6_hdr(skb); + const struct ipv6hdr *iph = ipv6_hdr(skb); struct dst_entry *dst; struct flowi6 fl6 = { .flowi6_oif = skb->sk ? skb->sk->sk_bound_dev_if : 0, @@ -67,7 +67,7 @@ static void nf_ip6_saveroute(const struct sk_buff *skb, struct ip6_rt_info *rt_info = nf_queue_entry_reroute(entry); if (entry->hook == NF_INET_LOCAL_OUT) { - struct ipv6hdr *iph = ipv6_hdr(skb); + const struct ipv6hdr *iph = ipv6_hdr(skb); rt_info->daddr = iph->daddr; rt_info->saddr = iph->saddr; @@ -81,7 +81,7 @@ static int nf_ip6_reroute(struct sk_buff *skb, struct ip6_rt_info *rt_info = nf_queue_entry_reroute(entry); if (entry->hook == NF_INET_LOCAL_OUT) { - struct ipv6hdr *iph = ipv6_hdr(skb); + const struct ipv6hdr *iph = ipv6_hdr(skb); if (!ipv6_addr_equal(&iph->daddr, &rt_info->daddr) || !ipv6_addr_equal(&iph->saddr, &rt_info->saddr) || skb->mark != rt_info->mark) @@ -108,7 +108,7 @@ static int nf_ip6_route(struct net *net, struct dst_entry **dst, __sum16 nf_ip6_checksum(struct sk_buff *skb, unsigned int hook, unsigned int dataoff, u_int8_t protocol) { - struct ipv6hdr *ip6h = ipv6_hdr(skb); + const struct ipv6hdr *ip6h = ipv6_hdr(skb); __sum16 csum = 0; switch (skb->ip_summed) { @@ -142,7 +142,7 @@ static __sum16 nf_ip6_checksum_partial(struct sk_buff *skb, unsigned int hook, unsigned int dataoff, unsigned int len, u_int8_t protocol) { - struct ipv6hdr *ip6h = ipv6_hdr(skb); + const struct ipv6hdr *ip6h = ipv6_hdr(skb); __wsum hsum; __sum16 csum = 0; diff --git a/net/ipv6/netfilter/ip6t_REJECT.c b/net/ipv6/netfilter/ip6t_REJECT.c index 28e74488a32..a5a4c5dd539 100644 --- a/net/ipv6/netfilter/ip6t_REJECT.c +++ b/net/ipv6/netfilter/ip6t_REJECT.c @@ -45,6 +45,8 @@ static void send_reset(struct net *net, struct sk_buff *oldskb) int tcphoff, needs_ack; const struct ipv6hdr *oip6h = ipv6_hdr(oldskb); struct ipv6hdr *ip6h; +#define DEFAULT_TOS_VALUE 0x0U + const __u8 tclass = DEFAULT_TOS_VALUE; struct dst_entry *dst = NULL; u8 proto; struct flowi6 fl6; @@ -124,7 +126,7 @@ static void send_reset(struct net *net, struct sk_buff *oldskb) skb_put(nskb, sizeof(struct ipv6hdr)); skb_reset_network_header(nskb); ip6h = ipv6_hdr(nskb); - ip6h->version = 6; + *(__be32 *)ip6h = htonl(0x60000000 | (tclass << 20)); ip6h->hop_limit = ip6_dst_hoplimit(dst); ip6h->nexthdr = IPPROTO_TCP; ipv6_addr_copy(&ip6h->saddr, &oip6h->daddr); diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c index 24b3558b8e6..18ff5df7ec0 100644 --- a/net/ipv6/proc.c +++ b/net/ipv6/proc.c @@ -141,7 +141,11 @@ static const struct snmp_mib snmp6_udplite6_list[] = { SNMP_MIB_SENTINEL }; -static void snmp6_seq_show_icmpv6msg(struct seq_file *seq, void __percpu **mib) +/* can be called either with percpu mib (pcpumib != NULL), + * or shared one (smib != NULL) + */ +static void snmp6_seq_show_icmpv6msg(struct seq_file *seq, void __percpu **pcpumib, + atomic_long_t *smib) { char name[32]; int i; @@ -158,14 +162,14 @@ static void snmp6_seq_show_icmpv6msg(struct seq_file *seq, void __percpu **mib) snprintf(name, sizeof(name), "Icmp6%s%s", i & 0x100 ? "Out" : "In", p); seq_printf(seq, "%-32s\t%lu\n", name, - snmp_fold_field(mib, i)); + pcpumib ? snmp_fold_field(pcpumib, i) : atomic_long_read(smib + i)); } /* print by number (nonzero only) - ICMPMsgStat format */ for (i = 0; i < ICMP6MSG_MIB_MAX; i++) { unsigned long val; - val = snmp_fold_field(mib, i); + val = pcpumib ? snmp_fold_field(pcpumib, i) : atomic_long_read(smib + i); if (!val) continue; snprintf(name, sizeof(name), "Icmp6%sType%u", @@ -174,14 +178,22 @@ static void snmp6_seq_show_icmpv6msg(struct seq_file *seq, void __percpu **mib) } } -static void snmp6_seq_show_item(struct seq_file *seq, void __percpu **mib, +/* can be called either with percpu mib (pcpumib != NULL), + * or shared one (smib != NULL) + */ +static void snmp6_seq_show_item(struct seq_file *seq, void __percpu **pcpumib, + atomic_long_t *smib, const struct snmp_mib *itemlist) { int i; + unsigned long val; - for (i = 0; itemlist[i].name; i++) - seq_printf(seq, "%-32s\t%lu\n", itemlist[i].name, - snmp_fold_field(mib, itemlist[i].entry)); + for (i = 0; itemlist[i].name; i++) { + val = pcpumib ? + snmp_fold_field(pcpumib, itemlist[i].entry) : + atomic_long_read(smib + itemlist[i].entry); + seq_printf(seq, "%-32s\t%lu\n", itemlist[i].name, val); + } } static void snmp6_seq_show_item64(struct seq_file *seq, void __percpu **mib, @@ -201,13 +213,13 @@ static int snmp6_seq_show(struct seq_file *seq, void *v) snmp6_seq_show_item64(seq, (void __percpu **)net->mib.ipv6_statistics, snmp6_ipstats_list, offsetof(struct ipstats_mib, syncp)); snmp6_seq_show_item(seq, (void __percpu **)net->mib.icmpv6_statistics, - snmp6_icmp6_list); + NULL, snmp6_icmp6_list); snmp6_seq_show_icmpv6msg(seq, - (void __percpu **)net->mib.icmpv6msg_statistics); + (void __percpu **)net->mib.icmpv6msg_statistics, NULL); snmp6_seq_show_item(seq, (void __percpu **)net->mib.udp_stats_in6, - snmp6_udp6_list); + NULL, snmp6_udp6_list); snmp6_seq_show_item(seq, (void __percpu **)net->mib.udplite_stats_in6, - snmp6_udplite6_list); + NULL, snmp6_udplite6_list); return 0; } @@ -229,11 +241,11 @@ static int snmp6_dev_seq_show(struct seq_file *seq, void *v) struct inet6_dev *idev = (struct inet6_dev *)seq->private; seq_printf(seq, "%-32s\t%u\n", "ifIndex", idev->dev->ifindex); - snmp6_seq_show_item(seq, (void __percpu **)idev->stats.ipv6, + snmp6_seq_show_item(seq, (void __percpu **)idev->stats.ipv6, NULL, snmp6_ipstats_list); - snmp6_seq_show_item(seq, (void __percpu **)idev->stats.icmpv6, + snmp6_seq_show_item(seq, NULL, idev->stats.icmpv6dev->mibs, snmp6_icmp6_list); - snmp6_seq_show_icmpv6msg(seq, (void __percpu **)idev->stats.icmpv6msg); + snmp6_seq_show_icmpv6msg(seq, NULL, idev->stats.icmpv6msgdev->mibs); return 0; } diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 4a1c3b46c56..cc7313b8f7e 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -67,8 +67,8 @@ static struct raw_hashinfo raw_v6_hashinfo = { }; static struct sock *__raw_v6_lookup(struct net *net, struct sock *sk, - unsigned short num, struct in6_addr *loc_addr, - struct in6_addr *rmt_addr, int dif) + unsigned short num, const struct in6_addr *loc_addr, + const struct in6_addr *rmt_addr, int dif) { struct hlist_node *node; int is_multicast = ipv6_addr_is_multicast(loc_addr); @@ -154,8 +154,8 @@ EXPORT_SYMBOL(rawv6_mh_filter_unregister); */ static int ipv6_raw_deliver(struct sk_buff *skb, int nexthdr) { - struct in6_addr *saddr; - struct in6_addr *daddr; + const struct in6_addr *saddr; + const struct in6_addr *daddr; struct sock *sk; int delivered = 0; __u8 hash; @@ -348,7 +348,7 @@ void raw6_icmp_error(struct sk_buff *skb, int nexthdr, { struct sock *sk; int hash; - struct in6_addr *saddr, *daddr; + const struct in6_addr *saddr, *daddr; struct net *net; hash = nexthdr & (RAW_HTABLE_SIZE - 1); @@ -357,7 +357,7 @@ void raw6_icmp_error(struct sk_buff *skb, int nexthdr, sk = sk_head(&raw_v6_hashinfo.ht[hash]); if (sk != NULL) { /* Note: ipv6_hdr(skb) != skb->data */ - struct ipv6hdr *ip6h = (struct ipv6hdr *)skb->data; + const struct ipv6hdr *ip6h = (const struct ipv6hdr *)skb->data; saddr = &ip6h->saddr; daddr = &ip6h->daddr; net = dev_net(skb->dev); @@ -542,8 +542,8 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, goto out; offset = rp->offset; - total_len = inet_sk(sk)->cork.length - (skb_network_header(skb) - - skb->data); + total_len = inet_sk(sk)->cork.base.length - (skb_network_header(skb) - + skb->data); if (offset >= total_len - 1) { err = -EINVAL; ip6_flush_pending_frames(sk); @@ -1231,7 +1231,7 @@ struct proto rawv6_prot = { static void raw6_sock_seq_show(struct seq_file *seq, struct sock *sp, int i) { struct ipv6_pinfo *np = inet6_sk(sp); - struct in6_addr *dest, *src; + const struct in6_addr *dest, *src; __u16 destp, srcp; dest = &np->daddr; @@ -1240,7 +1240,7 @@ static void raw6_sock_seq_show(struct seq_file *seq, struct sock *sp, int i) srcp = inet_sk(sp)->inet_num; seq_printf(seq, "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X " - "%02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p %d\n", + "%02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %pK %d\n", i, src->s6_addr32[0], src->s6_addr32[1], src->s6_addr32[2], src->s6_addr32[3], srcp, diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 07beeb06f75..7b954e2539d 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -224,7 +224,7 @@ out: } static __inline__ struct frag_queue * -fq_find(struct net *net, __be32 id, struct in6_addr *src, struct in6_addr *dst) +fq_find(struct net *net, __be32 id, const struct in6_addr *src, const struct in6_addr *dst) { struct inet_frag_queue *q; struct ip6_create_arg arg; @@ -535,7 +535,7 @@ static int ipv6_frag_rcv(struct sk_buff *skb) { struct frag_hdr *fhdr; struct frag_queue *fq; - struct ipv6hdr *hdr = ipv6_hdr(skb); + const struct ipv6hdr *hdr = ipv6_hdr(skb); struct net *net = dev_net(skb_dst(skb)->dev); IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_REASMREQDS); diff --git a/net/ipv6/route.c b/net/ipv6/route.c index af26cc1073c..216ff31a0cc 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -89,12 +89,12 @@ static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu); #ifdef CONFIG_IPV6_ROUTE_INFO static struct rt6_info *rt6_add_route_info(struct net *net, - struct in6_addr *prefix, int prefixlen, - struct in6_addr *gwaddr, int ifindex, + const struct in6_addr *prefix, int prefixlen, + const struct in6_addr *gwaddr, int ifindex, unsigned pref); static struct rt6_info *rt6_get_route_info(struct net *net, - struct in6_addr *prefix, int prefixlen, - struct in6_addr *gwaddr, int ifindex); + const struct in6_addr *prefix, int prefixlen, + const struct in6_addr *gwaddr, int ifindex); #endif static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old) @@ -153,6 +153,12 @@ static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, u32 mtu) { } +static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst, + unsigned long old) +{ + return NULL; +} + static struct dst_ops ip6_dst_blackhole_ops = { .family = AF_INET6, .protocol = cpu_to_be16(ETH_P_IPV6), @@ -161,6 +167,7 @@ static struct dst_ops ip6_dst_blackhole_ops = { .default_mtu = ip6_blackhole_default_mtu, .default_advmss = ip6_default_advmss, .update_pmtu = ip6_rt_blackhole_update_pmtu, + .cow_metrics = ip6_rt_blackhole_cow_metrics, }; static const u32 ip6_template_metrics[RTAX_MAX] = { @@ -220,9 +227,14 @@ static struct rt6_info ip6_blk_hole_entry_template = { #endif /* allocate dst with ip6_dst_ops */ -static inline struct rt6_info *ip6_dst_alloc(struct dst_ops *ops) +static inline struct rt6_info *ip6_dst_alloc(struct dst_ops *ops, + struct net_device *dev) { - return (struct rt6_info *)dst_alloc(ops, 0); + struct rt6_info *rt = dst_alloc(ops, dev, 0, 0, 0); + + memset(&rt->rt6i_table, 0, sizeof(*rt) - sizeof(struct dst_entry)); + + return rt; } static void ip6_dst_destroy(struct dst_entry *dst) @@ -283,7 +295,7 @@ static __inline__ int rt6_check_expired(const struct rt6_info *rt) time_after(jiffies, rt->rt6i_expires); } -static inline int rt6_need_strict(struct in6_addr *daddr) +static inline int rt6_need_strict(const struct in6_addr *daddr) { return ipv6_addr_type(daddr) & (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK); @@ -295,7 +307,7 @@ static inline int rt6_need_strict(struct in6_addr *daddr) static inline struct rt6_info *rt6_device_match(struct net *net, struct rt6_info *rt, - struct in6_addr *saddr, + const struct in6_addr *saddr, int oif, int flags) { @@ -507,7 +519,7 @@ static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict) #ifdef CONFIG_IPV6_ROUTE_INFO int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, - struct in6_addr *gwaddr) + const struct in6_addr *gwaddr) { struct net *net = dev_net(dev); struct route_info *rinfo = (struct route_info *) opt; @@ -670,8 +682,8 @@ int ip6_ins_rt(struct rt6_info *rt) return __ip6_ins_rt(rt, &info); } -static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, struct in6_addr *daddr, - struct in6_addr *saddr) +static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, const struct in6_addr *daddr, + const struct in6_addr *saddr) { struct rt6_info *rt; @@ -739,7 +751,7 @@ static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, struct in6_addr *dad return rt; } -static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort, struct in6_addr *daddr) +static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort, const struct in6_addr *daddr) { struct rt6_info *rt = ip6_rt_copy(ort); if (rt) { @@ -830,7 +842,7 @@ static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table * void ip6_route_input(struct sk_buff *skb) { - struct ipv6hdr *iph = ipv6_hdr(skb); + const struct ipv6hdr *iph = ipv6_hdr(skb); struct net *net = dev_net(skb->dev); int flags = RT6_LOOKUP_F_HAS_SADDR; struct flowi6 fl6 = { @@ -874,11 +886,13 @@ EXPORT_SYMBOL(ip6_route_output); struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig) { - struct rt6_info *rt = dst_alloc(&ip6_dst_blackhole_ops, 1); - struct rt6_info *ort = (struct rt6_info *) dst_orig; + struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig; struct dst_entry *new = NULL; + rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, 0, 0); if (rt) { + memset(&rt->rt6i_table, 0, sizeof(*rt) - sizeof(struct dst_entry)); + new = &rt->dst; new->__use = 1; @@ -886,9 +900,6 @@ struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_ori new->output = dst_discard; dst_copy_metrics(new, &ort->dst); - new->dev = ort->dst.dev; - if (new->dev) - dev_hold(new->dev); rt->rt6i_idev = ort->rt6i_idev; if (rt->rt6i_idev) in6_dev_hold(rt->rt6i_idev); @@ -1031,13 +1042,12 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev, if (unlikely(idev == NULL)) return NULL; - rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops); + rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops, dev); if (unlikely(rt == NULL)) { in6_dev_put(idev); goto out; } - dev_hold(dev); if (neigh) neigh_hold(neigh); else { @@ -1046,7 +1056,6 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev, neigh = NULL; } - rt->rt6i_dev = dev; rt->rt6i_idev = idev; rt->rt6i_nexthop = neigh; atomic_set(&rt->dst.__refcnt, 1); @@ -1205,7 +1214,7 @@ int ip6_route_add(struct fib6_config *cfg) goto out; } - rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops); + rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops, NULL); if (rt == NULL) { err = -ENOMEM; @@ -1272,7 +1281,7 @@ int ip6_route_add(struct fib6_config *cfg) } if (cfg->fc_flags & RTF_GATEWAY) { - struct in6_addr *gw_addr; + const struct in6_addr *gw_addr; int gwa_type; gw_addr = &cfg->fc_gateway; @@ -1512,9 +1521,9 @@ out: return rt; }; -static struct rt6_info *ip6_route_redirect(struct in6_addr *dest, - struct in6_addr *src, - struct in6_addr *gateway, +static struct rt6_info *ip6_route_redirect(const struct in6_addr *dest, + const struct in6_addr *src, + const struct in6_addr *gateway, struct net_device *dev) { int flags = RT6_LOOKUP_F_HAS_SADDR; @@ -1536,8 +1545,8 @@ static struct rt6_info *ip6_route_redirect(struct in6_addr *dest, flags, __ip6_route_redirect); } -void rt6_redirect(struct in6_addr *dest, struct in6_addr *src, - struct in6_addr *saddr, +void rt6_redirect(const struct in6_addr *dest, const struct in6_addr *src, + const struct in6_addr *saddr, struct neighbour *neigh, u8 *lladdr, int on_link) { struct rt6_info *rt, *nrt = NULL; @@ -1611,7 +1620,7 @@ out: * i.e. Path MTU discovery */ -static void rt6_do_pmtu_disc(struct in6_addr *daddr, struct in6_addr *saddr, +static void rt6_do_pmtu_disc(const struct in6_addr *daddr, const struct in6_addr *saddr, struct net *net, u32 pmtu, int ifindex) { struct rt6_info *rt, *nrt; @@ -1696,7 +1705,7 @@ out: dst_release(&rt->dst); } -void rt6_pmtu_discovery(struct in6_addr *daddr, struct in6_addr *saddr, +void rt6_pmtu_discovery(const struct in6_addr *daddr, const struct in6_addr *saddr, struct net_device *dev, u32 pmtu) { struct net *net = dev_net(dev); @@ -1724,7 +1733,8 @@ void rt6_pmtu_discovery(struct in6_addr *daddr, struct in6_addr *saddr, static struct rt6_info * ip6_rt_copy(struct rt6_info *ort) { struct net *net = dev_net(ort->rt6i_dev); - struct rt6_info *rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops); + struct rt6_info *rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops, + ort->dst.dev); if (rt) { rt->dst.input = ort->dst.input; @@ -1732,9 +1742,6 @@ static struct rt6_info * ip6_rt_copy(struct rt6_info *ort) dst_copy_metrics(&rt->dst, &ort->dst); rt->dst.error = ort->dst.error; - rt->dst.dev = ort->dst.dev; - if (rt->dst.dev) - dev_hold(rt->dst.dev); rt->rt6i_idev = ort->rt6i_idev; if (rt->rt6i_idev) in6_dev_hold(rt->rt6i_idev); @@ -1749,6 +1756,7 @@ static struct rt6_info * ip6_rt_copy(struct rt6_info *ort) #ifdef CONFIG_IPV6_SUBTREES memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); #endif + memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key)); rt->rt6i_table = ort->rt6i_table; } return rt; @@ -1756,8 +1764,8 @@ static struct rt6_info * ip6_rt_copy(struct rt6_info *ort) #ifdef CONFIG_IPV6_ROUTE_INFO static struct rt6_info *rt6_get_route_info(struct net *net, - struct in6_addr *prefix, int prefixlen, - struct in6_addr *gwaddr, int ifindex) + const struct in6_addr *prefix, int prefixlen, + const struct in6_addr *gwaddr, int ifindex) { struct fib6_node *fn; struct rt6_info *rt = NULL; @@ -1788,8 +1796,8 @@ out: } static struct rt6_info *rt6_add_route_info(struct net *net, - struct in6_addr *prefix, int prefixlen, - struct in6_addr *gwaddr, int ifindex, + const struct in6_addr *prefix, int prefixlen, + const struct in6_addr *gwaddr, int ifindex, unsigned pref) { struct fib6_config cfg = { @@ -1817,7 +1825,7 @@ static struct rt6_info *rt6_add_route_info(struct net *net, } #endif -struct rt6_info *rt6_get_dflt_router(struct in6_addr *addr, struct net_device *dev) +struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev) { struct rt6_info *rt; struct fib6_table *table; @@ -1839,7 +1847,7 @@ struct rt6_info *rt6_get_dflt_router(struct in6_addr *addr, struct net_device *d return rt; } -struct rt6_info *rt6_add_dflt_router(struct in6_addr *gwaddr, +struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, struct net_device *dev, unsigned int pref) { @@ -2004,7 +2012,8 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, int anycast) { struct net *net = dev_net(idev->dev); - struct rt6_info *rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops); + struct rt6_info *rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops, + net->loopback_dev); struct neighbour *neigh; if (rt == NULL) { @@ -2014,15 +2023,12 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, return ERR_PTR(-ENOMEM); } - dev_hold(net->loopback_dev); in6_dev_hold(idev); rt->dst.flags = DST_HOST; rt->dst.input = ip6_input; rt->dst.output = ip6_output; - rt->rt6i_dev = net->loopback_dev; rt->rt6i_idev = idev; - dst_metric_set(&rt->dst, RTAX_HOPLIMIT, -1); rt->dst.obsolete = -1; rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP; @@ -2049,7 +2055,7 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, int ip6_route_get_saddr(struct net *net, struct rt6_info *rt, - struct in6_addr *daddr, + const struct in6_addr *daddr, unsigned int prefs, struct in6_addr *saddr) { @@ -2919,9 +2925,9 @@ int __init ip6_route_init(void) goto xfrm6_init; ret = -ENOBUFS; - if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL) || - __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL) || - __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL)) + if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) || + __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) || + __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL)) goto fib6_rules_init; ret = register_netdevice_notifier(&ip6_route_dev_notifier); diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 43b33373adb..1cca5761aea 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -250,11 +250,6 @@ static struct ip_tunnel *ipip6_tunnel_locate(struct net *net, dev_net_set(dev, net); - if (strchr(name, '%')) { - if (dev_alloc_name(dev, name) < 0) - goto failed_free; - } - nt = netdev_priv(dev); nt->parms = *parms; @@ -401,11 +396,6 @@ out: return err; } -static void prl_entry_destroy_rcu(struct rcu_head *head) -{ - kfree(container_of(head, struct ip_tunnel_prl_entry, rcu_head)); -} - static void prl_list_destroy_rcu(struct rcu_head *head) { struct ip_tunnel_prl_entry *p, *n; @@ -433,7 +423,7 @@ ipip6_tunnel_del_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a) p = &x->next) { if (x->addr == a->addr) { *p = x->next; - call_rcu(&x->rcu_head, prl_entry_destroy_rcu); + kfree_rcu(x, rcu_head); t->prl_count--; goto out; } @@ -452,7 +442,7 @@ out: } static int -isatap_chksrc(struct sk_buff *skb, struct iphdr *iph, struct ip_tunnel *t) +isatap_chksrc(struct sk_buff *skb, const struct iphdr *iph, struct ip_tunnel *t) { struct ip_tunnel_prl_entry *p; int ok = 1; @@ -465,7 +455,8 @@ isatap_chksrc(struct sk_buff *skb, struct iphdr *iph, struct ip_tunnel *t) else skb->ndisc_nodetype = NDISC_NODETYPE_NODEFAULT; } else { - struct in6_addr *addr6 = &ipv6_hdr(skb)->saddr; + const struct in6_addr *addr6 = &ipv6_hdr(skb)->saddr; + if (ipv6_addr_is_isatap(addr6) && (addr6->s6_addr32[3] == iph->saddr) && ipv6_chk_prefix(addr6, t->dev)) @@ -499,7 +490,7 @@ static int ipip6_err(struct sk_buff *skb, u32 info) 8 bytes of packet payload. It means, that precise relaying of ICMP in the real Internet is absolutely infeasible. */ - struct iphdr *iph = (struct iphdr*)skb->data; + const struct iphdr *iph = (const struct iphdr *)skb->data; const int type = icmp_hdr(skb)->type; const int code = icmp_hdr(skb)->code; struct ip_tunnel *t; @@ -557,7 +548,7 @@ out: return err; } -static inline void ipip6_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb) +static inline void ipip6_ecn_decapsulate(const struct iphdr *iph, struct sk_buff *skb) { if (INET_ECN_is_ce(iph->tos)) IP6_ECN_set_ce(ipv6_hdr(skb)); @@ -565,7 +556,7 @@ static inline void ipip6_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb) static int ipip6_rcv(struct sk_buff *skb) { - struct iphdr *iph; + const struct iphdr *iph; struct ip_tunnel *tunnel; if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) @@ -621,7 +612,7 @@ out: * comes from 6rd / 6to4 (RFC 3056) addr space. */ static inline -__be32 try_6rd(struct in6_addr *v6dst, struct ip_tunnel *tunnel) +__be32 try_6rd(const struct in6_addr *v6dst, struct ip_tunnel *tunnel) { __be32 dst = 0; @@ -664,8 +655,8 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, { struct ip_tunnel *tunnel = netdev_priv(dev); struct pcpu_tstats *tstats; - struct iphdr *tiph = &tunnel->parms.iph; - struct ipv6hdr *iph6 = ipv6_hdr(skb); + const struct iphdr *tiph = &tunnel->parms.iph; + const struct ipv6hdr *iph6 = ipv6_hdr(skb); u8 tos = tunnel->parms.iph.tos; __be16 df = tiph->frag_off; struct rtable *rt; /* Route to the other host */ @@ -673,8 +664,9 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, struct iphdr *iph; /* Our new IP header */ unsigned int max_headroom; /* The extra header space needed */ __be32 dst = tiph->daddr; + struct flowi4 fl4; int mtu; - struct in6_addr *addr6; + const struct in6_addr *addr6; int addr_type; if (skb->protocol != htons(ETH_P_IPV6)) @@ -693,7 +685,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, goto tx_error; } - addr6 = (struct in6_addr*)&neigh->primary_key; + addr6 = (const struct in6_addr*)&neigh->primary_key; addr_type = ipv6_addr_type(addr6); if ((addr_type & IPV6_ADDR_UNICAST) && @@ -718,7 +710,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, goto tx_error; } - addr6 = (struct in6_addr*)&neigh->primary_key; + addr6 = (const struct in6_addr*)&neigh->primary_key; addr_type = ipv6_addr_type(addr6); if (addr_type == IPV6_ADDR_ANY) { @@ -732,7 +724,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, dst = addr6->s6_addr32[3]; } - rt = ip_route_output_ports(dev_net(dev), NULL, + rt = ip_route_output_ports(dev_net(dev), &fl4, NULL, dst, tiph->saddr, 0, 0, IPPROTO_IPV6, RT_TOS(tos), @@ -826,8 +818,8 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, iph->frag_off = df; iph->protocol = IPPROTO_IPV6; iph->tos = INET_ECN_encapsulate(tos, ipv6_get_dsfield(iph6)); - iph->daddr = rt->rt_dst; - iph->saddr = rt->rt_src; + iph->daddr = fl4.daddr; + iph->saddr = fl4.saddr; if ((iph->ttl = tiph->ttl) == 0) iph->ttl = iph6->hop_limit; @@ -849,13 +841,14 @@ static void ipip6_tunnel_bind_dev(struct net_device *dev) { struct net_device *tdev = NULL; struct ip_tunnel *tunnel; - struct iphdr *iph; + const struct iphdr *iph; + struct flowi4 fl4; tunnel = netdev_priv(dev); iph = &tunnel->parms.iph; if (iph->daddr) { - struct rtable *rt = ip_route_output_ports(dev_net(dev), NULL, + struct rtable *rt = ip_route_output_ports(dev_net(dev), &fl4, NULL, iph->daddr, iph->saddr, 0, 0, IPPROTO_IPV6, diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index 352c26081f5..89d5bf80622 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -66,7 +66,7 @@ static inline struct sock *get_cookie_sock(struct sock *sk, struct sk_buff *skb, static DEFINE_PER_CPU(__u32 [16 + 5 + SHA_WORKSPACE_WORDS], ipv6_cookie_scratch); -static u32 cookie_hash(struct in6_addr *saddr, struct in6_addr *daddr, +static u32 cookie_hash(const struct in6_addr *saddr, const struct in6_addr *daddr, __be16 sport, __be16 dport, u32 count, int c) { __u32 *tmp = __get_cpu_var(ipv6_cookie_scratch); @@ -86,7 +86,8 @@ static u32 cookie_hash(struct in6_addr *saddr, struct in6_addr *daddr, return tmp[17]; } -static __u32 secure_tcp_syn_cookie(struct in6_addr *saddr, struct in6_addr *daddr, +static __u32 secure_tcp_syn_cookie(const struct in6_addr *saddr, + const struct in6_addr *daddr, __be16 sport, __be16 dport, __u32 sseq, __u32 count, __u32 data) { @@ -96,8 +97,8 @@ static __u32 secure_tcp_syn_cookie(struct in6_addr *saddr, struct in6_addr *dadd & COOKIEMASK)); } -static __u32 check_tcp_syn_cookie(__u32 cookie, struct in6_addr *saddr, - struct in6_addr *daddr, __be16 sport, +static __u32 check_tcp_syn_cookie(__u32 cookie, const struct in6_addr *saddr, + const struct in6_addr *daddr, __be16 sport, __be16 dport, __u32 sseq, __u32 count, __u32 maxdiff) { @@ -116,7 +117,7 @@ static __u32 check_tcp_syn_cookie(__u32 cookie, struct in6_addr *saddr, __u32 cookie_v6_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp) { - struct ipv6hdr *iph = ipv6_hdr(skb); + const struct ipv6hdr *iph = ipv6_hdr(skb); const struct tcphdr *th = tcp_hdr(skb); int mssind; const __u16 mss = *mssp; @@ -138,7 +139,7 @@ __u32 cookie_v6_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp) static inline int cookie_check(struct sk_buff *skb, __u32 cookie) { - struct ipv6hdr *iph = ipv6_hdr(skb); + const struct ipv6hdr *iph = ipv6_hdr(skb); const struct tcphdr *th = tcp_hdr(skb); __u32 seq = ntohl(th->seq) - 1; __u32 mssind = check_tcp_syn_cookie(cookie, &iph->saddr, &iph->daddr, @@ -222,6 +223,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) ireq->wscale_ok = tcp_opt.wscale_ok; ireq->tstamp_ok = tcp_opt.saw_tstamp; req->ts_recent = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsval : 0; + treq->snt_synack = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsecr : 0; treq->rcv_isn = ntohl(th->seq) - 1; treq->snt_isn = cookie; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 4f49e5dd41b..a1ef61a889c 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -76,8 +76,8 @@ static void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb); static void __tcp_v6_send_check(struct sk_buff *skb, - struct in6_addr *saddr, - struct in6_addr *daddr); + const struct in6_addr *saddr, + const struct in6_addr *daddr); static const struct inet_connection_sock_af_ops ipv6_mapped; static const struct inet_connection_sock_af_ops ipv6_specific; @@ -86,7 +86,7 @@ static const struct tcp_sock_af_ops tcp_sock_ipv6_specific; static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific; #else static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(struct sock *sk, - struct in6_addr *addr) + const struct in6_addr *addr) { return NULL; } @@ -106,8 +106,8 @@ static void tcp_v6_hash(struct sock *sk) } static __inline__ __sum16 tcp_v6_check(int len, - struct in6_addr *saddr, - struct in6_addr *daddr, + const struct in6_addr *saddr, + const struct in6_addr *daddr, __wsum base) { return csum_ipv6_magic(saddr, daddr, len, IPPROTO_TCP, base); @@ -331,7 +331,7 @@ failure: static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, u8 type, u8 code, int offset, __be32 info) { - struct ipv6hdr *hdr = (struct ipv6hdr*)skb->data; + const struct ipv6hdr *hdr = (const struct ipv6hdr*)skb->data; const struct tcphdr *th = (struct tcphdr *)(skb->data+offset); struct ipv6_pinfo *np; struct sock *sk; @@ -551,7 +551,7 @@ static void tcp_v6_reqsk_destructor(struct request_sock *req) #ifdef CONFIG_TCP_MD5SIG static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(struct sock *sk, - struct in6_addr *addr) + const struct in6_addr *addr) { struct tcp_sock *tp = tcp_sk(sk); int i; @@ -580,7 +580,7 @@ static struct tcp_md5sig_key *tcp_v6_reqsk_md5_lookup(struct sock *sk, return tcp_v6_md5_do_lookup(sk, &inet6_rsk(req)->rmt_addr); } -static int tcp_v6_md5_do_add(struct sock *sk, struct in6_addr *peer, +static int tcp_v6_md5_do_add(struct sock *sk, const struct in6_addr *peer, char *newkey, u8 newkeylen) { /* Add key to the list */ @@ -645,7 +645,7 @@ static int tcp_v6_md5_add_func(struct sock *sk, struct sock *addr_sk, newkey, newkeylen); } -static int tcp_v6_md5_do_del(struct sock *sk, struct in6_addr *peer) +static int tcp_v6_md5_do_del(struct sock *sk, const struct in6_addr *peer) { struct tcp_sock *tp = tcp_sk(sk); int i; @@ -753,8 +753,8 @@ static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval, } static int tcp_v6_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp, - struct in6_addr *daddr, - struct in6_addr *saddr, int nbytes) + const struct in6_addr *daddr, + const struct in6_addr *saddr, int nbytes) { struct tcp6_pseudohdr *bp; struct scatterlist sg; @@ -771,7 +771,7 @@ static int tcp_v6_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp, } static int tcp_v6_md5_hash_hdr(char *md5_hash, struct tcp_md5sig_key *key, - struct in6_addr *daddr, struct in6_addr *saddr, + const struct in6_addr *daddr, struct in6_addr *saddr, struct tcphdr *th) { struct tcp_md5sig_pool *hp; @@ -807,7 +807,7 @@ static int tcp_v6_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key, struct sock *sk, struct request_sock *req, struct sk_buff *skb) { - struct in6_addr *saddr, *daddr; + const struct in6_addr *saddr, *daddr; struct tcp_md5sig_pool *hp; struct hash_desc *desc; struct tcphdr *th = tcp_hdr(skb); @@ -819,7 +819,7 @@ static int tcp_v6_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key, saddr = &inet6_rsk(req)->loc_addr; daddr = &inet6_rsk(req)->rmt_addr; } else { - struct ipv6hdr *ip6h = ipv6_hdr(skb); + const struct ipv6hdr *ip6h = ipv6_hdr(skb); saddr = &ip6h->saddr; daddr = &ip6h->daddr; } @@ -857,7 +857,7 @@ static int tcp_v6_inbound_md5_hash (struct sock *sk, struct sk_buff *skb) { __u8 *hash_location = NULL; struct tcp_md5sig_key *hash_expected; - struct ipv6hdr *ip6h = ipv6_hdr(skb); + const struct ipv6hdr *ip6h = ipv6_hdr(skb); struct tcphdr *th = tcp_hdr(skb); int genhash; u8 newhash[16]; @@ -915,7 +915,7 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = { #endif static void __tcp_v6_send_check(struct sk_buff *skb, - struct in6_addr *saddr, struct in6_addr *daddr) + const struct in6_addr *saddr, const struct in6_addr *daddr) { struct tcphdr *th = tcp_hdr(skb); @@ -939,7 +939,7 @@ static void tcp_v6_send_check(struct sock *sk, struct sk_buff *skb) static int tcp_v6_gso_send_check(struct sk_buff *skb) { - struct ipv6hdr *ipv6h; + const struct ipv6hdr *ipv6h; struct tcphdr *th; if (!pskb_may_pull(skb, sizeof(*th))) @@ -957,7 +957,7 @@ static int tcp_v6_gso_send_check(struct sk_buff *skb) static struct sk_buff **tcp6_gro_receive(struct sk_buff **head, struct sk_buff *skb) { - struct ipv6hdr *iph = skb_gro_network_header(skb); + const struct ipv6hdr *iph = skb_gro_network_header(skb); switch (skb->ip_summed) { case CHECKSUM_COMPLETE: @@ -978,7 +978,7 @@ static struct sk_buff **tcp6_gro_receive(struct sk_buff **head, static int tcp6_gro_complete(struct sk_buff *skb) { - struct ipv6hdr *iph = ipv6_hdr(skb); + const struct ipv6hdr *iph = ipv6_hdr(skb); struct tcphdr *th = tcp_hdr(skb); th->check = ~tcp_v6_check(skb->len - skb_transport_offset(skb), @@ -1341,6 +1341,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) } have_isn: tcp_rsk(req)->snt_isn = isn; + tcp_rsk(req)->snt_synack = tcp_time_stamp; security_inet_conn_request(sk, skb, req); @@ -1469,7 +1470,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, First: no IPv4 options. */ - newinet->opt = NULL; + newinet->inet_opt = NULL; newnp->ipv6_fl_list = NULL; /* Clone RX bits */ @@ -1509,6 +1510,10 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, tcp_sync_mss(newsk, dst_mtu(dst)); newtp->advmss = dst_metric_advmss(dst); tcp_initialize_rcv_mss(newsk); + if (tcp_rsk(req)->snt_synack) + tcp_valid_rtt_meas(newsk, + tcp_time_stamp - tcp_rsk(req)->snt_synack); + newtp->total_retrans = req->retrans; newinet->inet_daddr = newinet->inet_saddr = LOOPBACK4_IPV6; newinet->inet_rcv_saddr = LOOPBACK4_IPV6; @@ -1702,7 +1707,7 @@ ipv6_pktoptions: static int tcp_v6_rcv(struct sk_buff *skb) { struct tcphdr *th; - struct ipv6hdr *hdr; + const struct ipv6hdr *hdr; struct sock *sk; int ret; struct net *net = dev_net(skb->dev); @@ -2028,15 +2033,15 @@ static void get_openreq6(struct seq_file *seq, struct sock *sk, struct request_sock *req, int i, int uid) { int ttd = req->expires - jiffies; - struct in6_addr *src = &inet6_rsk(req)->loc_addr; - struct in6_addr *dest = &inet6_rsk(req)->rmt_addr; + const struct in6_addr *src = &inet6_rsk(req)->loc_addr; + const struct in6_addr *dest = &inet6_rsk(req)->rmt_addr; if (ttd < 0) ttd = 0; seq_printf(seq, "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X " - "%02X %08X:%08X %02X:%08lX %08X %5d %8d %d %d %p\n", + "%02X %08X:%08X %02X:%08lX %08X %5d %8d %d %d %pK\n", i, src->s6_addr32[0], src->s6_addr32[1], src->s6_addr32[2], src->s6_addr32[3], @@ -2057,7 +2062,7 @@ static void get_openreq6(struct seq_file *seq, static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i) { - struct in6_addr *dest, *src; + const struct in6_addr *dest, *src; __u16 destp, srcp; int timer_active; unsigned long timer_expires; @@ -2087,7 +2092,7 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i) seq_printf(seq, "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X " - "%02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p %lu %lu %u %u %d\n", + "%02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %pK %lu %lu %u %u %d\n", i, src->s6_addr32[0], src->s6_addr32[1], src->s6_addr32[2], src->s6_addr32[3], srcp, @@ -2114,7 +2119,7 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i) static void get_timewait6_sock(struct seq_file *seq, struct inet_timewait_sock *tw, int i) { - struct in6_addr *dest, *src; + const struct in6_addr *dest, *src; __u16 destp, srcp; struct inet6_timewait_sock *tw6 = inet6_twsk((struct sock *)tw); int ttd = tw->tw_ttd - jiffies; @@ -2129,7 +2134,7 @@ static void get_timewait6_sock(struct seq_file *seq, seq_printf(seq, "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X " - "%02X %08X:%08X %02X:%08lX %08X %5d %8d %d %d %p\n", + "%02X %08X:%08X %02X:%08lX %08X %5d %8d %d %d %pK\n", i, src->s6_addr32[0], src->s6_addr32[1], src->s6_addr32[2], src->s6_addr32[3], srcp, diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 15c37746845..41f8c9c08db 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -311,7 +311,7 @@ static struct sock *__udp6_lib_lookup_skb(struct sk_buff *skb, struct udp_table *udptable) { struct sock *sk; - struct ipv6hdr *iph = ipv6_hdr(skb); + const struct ipv6hdr *iph = ipv6_hdr(skb); if (unlikely(sk = skb_steal_sock(skb))) return sk; @@ -463,9 +463,9 @@ void __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt, struct udp_table *udptable) { struct ipv6_pinfo *np; - struct ipv6hdr *hdr = (struct ipv6hdr*)skb->data; - struct in6_addr *saddr = &hdr->saddr; - struct in6_addr *daddr = &hdr->daddr; + const struct ipv6hdr *hdr = (const struct ipv6hdr *)skb->data; + const struct in6_addr *saddr = &hdr->saddr; + const struct in6_addr *daddr = &hdr->daddr; struct udphdr *uh = (struct udphdr*)(skb->data+offset); struct sock *sk; int err; @@ -553,8 +553,8 @@ drop_no_sk_drops_inc: } static struct sock *udp_v6_mcast_next(struct net *net, struct sock *sk, - __be16 loc_port, struct in6_addr *loc_addr, - __be16 rmt_port, struct in6_addr *rmt_addr, + __be16 loc_port, const struct in6_addr *loc_addr, + __be16 rmt_port, const struct in6_addr *rmt_addr, int dif) { struct hlist_nulls_node *node; @@ -633,7 +633,7 @@ drop: * so we don't need to lock the hashes. */ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb, - struct in6_addr *saddr, struct in6_addr *daddr, + const struct in6_addr *saddr, const struct in6_addr *daddr, struct udp_table *udptable) { struct sock *sk, *stack[256 / sizeof(struct sock *)]; @@ -716,7 +716,7 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, struct net *net = dev_net(skb->dev); struct sock *sk; struct udphdr *uh; - struct in6_addr *saddr, *daddr; + const struct in6_addr *saddr, *daddr; u32 ulen = 0; if (!pskb_may_pull(skb, sizeof(struct udphdr))) @@ -1278,7 +1278,7 @@ int compat_udpv6_getsockopt(struct sock *sk, int level, int optname, static int udp6_ufo_send_check(struct sk_buff *skb) { - struct ipv6hdr *ipv6h; + const struct ipv6hdr *ipv6h; struct udphdr *uh; if (!pskb_may_pull(skb, sizeof(*uh))) @@ -1328,14 +1328,14 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, u32 features) /* Do software UFO. Complete and fill in the UDP checksum as HW cannot * do checksum of UDP packets sent as multiple IP fragments. */ - offset = skb->csum_start - skb_headroom(skb); + offset = skb_checksum_start_offset(skb); csum = skb_checksum(skb, offset, skb->len- offset, 0); offset += skb->csum_offset; *(__sum16 *)(skb->data + offset) = csum_fold(csum); skb->ip_summed = CHECKSUM_NONE; /* Check if there is enough headroom to insert fragment header. */ - if ((skb_headroom(skb) < frag_hdr_sz) && + if ((skb_mac_header(skb) < skb->head + frag_hdr_sz) && pskb_expand_head(skb, frag_hdr_sz, 0, GFP_ATOMIC)) goto out; @@ -1382,7 +1382,7 @@ static void udp6_sock_seq_show(struct seq_file *seq, struct sock *sp, int bucket { struct inet_sock *inet = inet_sk(sp); struct ipv6_pinfo *np = inet6_sk(sp); - struct in6_addr *dest, *src; + const struct in6_addr *dest, *src; __u16 destp, srcp; dest = &np->daddr; @@ -1391,7 +1391,7 @@ static void udp6_sock_seq_show(struct seq_file *seq, struct sock *sp, int bucket srcp = ntohs(inet->inet_sport); seq_printf(seq, "%5d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X " - "%02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p %d\n", + "%02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %pK %d\n", bucket, src->s6_addr32[0], src->s6_addr32[1], src->s6_addr32[2], src->s6_addr32[3], srcp, diff --git a/net/ipv6/xfrm6_mode_beet.c b/net/ipv6/xfrm6_mode_beet.c index bbd48b101ba..3437d7d4eed 100644 --- a/net/ipv6/xfrm6_mode_beet.c +++ b/net/ipv6/xfrm6_mode_beet.c @@ -41,10 +41,8 @@ static int xfrm6_beet_output(struct xfrm_state *x, struct sk_buff *skb) { struct ipv6hdr *top_iph; struct ip_beet_phdr *ph; - struct iphdr *iphv4; int optlen, hdr_len; - iphv4 = ip_hdr(skb); hdr_len = 0; optlen = XFRM_MODE_SKB_CB(skb)->optlen; if (unlikely(optlen)) diff --git a/net/ipv6/xfrm6_mode_tunnel.c b/net/ipv6/xfrm6_mode_tunnel.c index 645cb968d45..4d6edff0498 100644 --- a/net/ipv6/xfrm6_mode_tunnel.c +++ b/net/ipv6/xfrm6_mode_tunnel.c @@ -20,7 +20,7 @@ static inline void ipip6_ecn_decapsulate(struct sk_buff *skb) { - struct ipv6hdr *outer_iph = ipv6_hdr(skb); + const struct ipv6hdr *outer_iph = ipv6_hdr(skb); struct ipv6hdr *inner_iph = ipipv6_hdr(skb); if (INET_ECN_is_ce(ipv6_get_dsfield(outer_iph))) @@ -55,8 +55,8 @@ static int xfrm6_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb) dsfield &= ~INET_ECN_MASK; ipv6_change_dsfield(top_iph, 0, dsfield); top_iph->hop_limit = ip6_dst_hoplimit(dst->child); - ipv6_addr_copy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr); - ipv6_addr_copy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr); + ipv6_addr_copy(&top_iph->saddr, (const struct in6_addr *)&x->props.saddr); + ipv6_addr_copy(&top_iph->daddr, (const struct in6_addr *)&x->id.daddr); return 0; } diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c index 8e688b3de9a..49a91c5f562 100644 --- a/net/ipv6/xfrm6_output.c +++ b/net/ipv6/xfrm6_output.c @@ -79,7 +79,7 @@ int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb) } EXPORT_SYMBOL(xfrm6_prepare_output); -static int xfrm6_output_finish(struct sk_buff *skb) +int xfrm6_output_finish(struct sk_buff *skb) { #ifdef CONFIG_NETFILTER IP6CB(skb)->flags |= IP6SKB_XFRM_TRANSFORMED; @@ -97,9 +97,9 @@ static int __xfrm6_output(struct sk_buff *skb) if ((x && x->props.mode == XFRM_MODE_TUNNEL) && ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) || dst_allfrag(skb_dst(skb)))) { - return ip6_fragment(skb, xfrm6_output_finish); + return ip6_fragment(skb, x->outer_mode->afinfo->output_finish); } - return xfrm6_output_finish(skb); + return x->outer_mode->afinfo->output_finish(skb); } int xfrm6_output(struct sk_buff *skb) diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index 05e34c8ec91..d879f7efbd1 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -124,7 +124,7 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse) struct flowi6 *fl6 = &fl->u.ip6; int onlyproto = 0; u16 offset = skb_network_header_len(skb); - struct ipv6hdr *hdr = ipv6_hdr(skb); + const struct ipv6hdr *hdr = ipv6_hdr(skb); struct ipv6_opt_hdr *exthdr; const unsigned char *nh = skb_network_header(skb); u8 nexthdr = nh[IP6CB(skb)->nhoff]; diff --git a/net/ipv6/xfrm6_state.c b/net/ipv6/xfrm6_state.c index afe941e9415..248f0b2a7ee 100644 --- a/net/ipv6/xfrm6_state.c +++ b/net/ipv6/xfrm6_state.c @@ -178,6 +178,7 @@ static struct xfrm_state_afinfo xfrm6_state_afinfo = { .tmpl_sort = __xfrm6_tmpl_sort, .state_sort = __xfrm6_state_sort, .output = xfrm6_output, + .output_finish = xfrm6_output_finish, .extract_input = xfrm6_extract_input, .extract_output = xfrm6_extract_output, .transport_finish = xfrm6_transport_finish, diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c index 2969cad408d..4fe1db12d2a 100644 --- a/net/ipv6/xfrm6_tunnel.c +++ b/net/ipv6/xfrm6_tunnel.c @@ -68,7 +68,7 @@ static DEFINE_SPINLOCK(xfrm6_tunnel_spi_lock); static struct kmem_cache *xfrm6_tunnel_spi_kmem __read_mostly; -static inline unsigned xfrm6_tunnel_spi_hash_byaddr(xfrm_address_t *addr) +static inline unsigned xfrm6_tunnel_spi_hash_byaddr(const xfrm_address_t *addr) { unsigned h; @@ -85,7 +85,7 @@ static inline unsigned xfrm6_tunnel_spi_hash_byspi(u32 spi) return spi % XFRM6_TUNNEL_SPI_BYSPI_HSIZE; } -static struct xfrm6_tunnel_spi *__xfrm6_tunnel_spi_lookup(struct net *net, xfrm_address_t *saddr) +static struct xfrm6_tunnel_spi *__xfrm6_tunnel_spi_lookup(struct net *net, const xfrm_address_t *saddr) { struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net); struct xfrm6_tunnel_spi *x6spi; @@ -101,7 +101,7 @@ static struct xfrm6_tunnel_spi *__xfrm6_tunnel_spi_lookup(struct net *net, xfrm_ return NULL; } -__be32 xfrm6_tunnel_spi_lookup(struct net *net, xfrm_address_t *saddr) +__be32 xfrm6_tunnel_spi_lookup(struct net *net, const xfrm_address_t *saddr) { struct xfrm6_tunnel_spi *x6spi; u32 spi; @@ -237,11 +237,11 @@ static int xfrm6_tunnel_input(struct xfrm_state *x, struct sk_buff *skb) static int xfrm6_tunnel_rcv(struct sk_buff *skb) { struct net *net = dev_net(skb->dev); - struct ipv6hdr *iph = ipv6_hdr(skb); + const struct ipv6hdr *iph = ipv6_hdr(skb); __be32 spi; - spi = xfrm6_tunnel_spi_lookup(net, (xfrm_address_t *)&iph->saddr); - return xfrm6_rcv_spi(skb, IPPROTO_IPV6, spi) > 0 ? : 0; + spi = xfrm6_tunnel_spi_lookup(net, (const xfrm_address_t *)&iph->saddr); + return xfrm6_rcv_spi(skb, IPPROTO_IPV6, spi); } static int xfrm6_tunnel_err(struct sk_buff *skb, struct inet6_skb_parm *opt, diff --git a/net/irda/ircomm/ircomm_core.c b/net/irda/ircomm/ircomm_core.c index e97082017f4..52079f19bbb 100644 --- a/net/irda/ircomm/ircomm_core.c +++ b/net/irda/ircomm/ircomm_core.c @@ -244,14 +244,8 @@ EXPORT_SYMBOL(ircomm_connect_request); void ircomm_connect_indication(struct ircomm_cb *self, struct sk_buff *skb, struct ircomm_info *info) { - int clen = 0; - IRDA_DEBUG(2, "%s()\n", __func__ ); - /* Check if the packet contains data on the control channel */ - if (skb->len > 0) - clen = skb->data[0]; - /* * If there are any data hiding in the control channel, we must * deliver it first. The side effect is that the control channel diff --git a/net/irda/ircomm/ircomm_lmp.c b/net/irda/ircomm/ircomm_lmp.c index 08fb54dc8c4..3b8095c771d 100644 --- a/net/irda/ircomm/ircomm_lmp.c +++ b/net/irda/ircomm/ircomm_lmp.c @@ -75,7 +75,6 @@ static int ircomm_lmp_connect_response(struct ircomm_cb *self, struct sk_buff *userdata) { struct sk_buff *tx_skb; - int ret; IRDA_DEBUG(0, "%s()\n", __func__ ); @@ -100,9 +99,7 @@ static int ircomm_lmp_connect_response(struct ircomm_cb *self, tx_skb = userdata; } - ret = irlmp_connect_response(self->lsap, tx_skb); - - return 0; + return irlmp_connect_response(self->lsap, tx_skb); } static int ircomm_lmp_disconnect_request(struct ircomm_cb *self, diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c index a39cca8331d..b3cc8b3989a 100644 --- a/net/irda/ircomm/ircomm_tty.c +++ b/net/irda/ircomm/ircomm_tty.c @@ -38,6 +38,7 @@ #include <linux/seq_file.h> #include <linux/termios.h> #include <linux/tty.h> +#include <linux/tty_flip.h> #include <linux/interrupt.h> #include <linux/device.h> /* for MODULE_ALIAS_CHARDEV_MAJOR */ @@ -1132,7 +1133,6 @@ static int ircomm_tty_data_indication(void *instance, void *sap, struct sk_buff *skb) { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - struct tty_ldisc *ld; IRDA_DEBUG(2, "%s()\n", __func__ ); @@ -1161,15 +1161,11 @@ static int ircomm_tty_data_indication(void *instance, void *sap, } /* - * Just give it over to the line discipline. There is no need to - * involve the flip buffers, since we are not running in an interrupt - * handler + * Use flip buffer functions since the code may be called from interrupt + * context */ - - ld = tty_ldisc_ref(self->tty); - if (ld) - ld->ops->receive_buf(self->tty, skb->data, NULL, skb->len); - tty_ldisc_deref(ld); + tty_insert_flip_string(self->tty, skb->data, skb->len); + tty_flip_buffer_push(self->tty); /* No need to kfree_skb - see ircomm_ttp_data_indication() */ diff --git a/net/irda/irlan/irlan_filter.c b/net/irda/irlan/irlan_filter.c index 9ff7823abec..7977be7caf0 100644 --- a/net/irda/irlan/irlan_filter.c +++ b/net/irda/irlan/irlan_filter.c @@ -143,12 +143,8 @@ void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb) */ void irlan_check_command_param(struct irlan_cb *self, char *param, char *value) { - __u8 *bytes; - IRDA_DEBUG(4, "%s()\n", __func__ ); - bytes = value; - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); diff --git a/net/irda/irlan/irlan_provider.c b/net/irda/irlan/irlan_provider.c index 5cf5e6c872b..b8af74ab8b6 100644 --- a/net/irda/irlan/irlan_provider.c +++ b/net/irda/irlan/irlan_provider.c @@ -128,7 +128,6 @@ static void irlan_provider_connect_indication(void *instance, void *sap, { struct irlan_cb *self; struct tsap_cb *tsap; - __u32 saddr, daddr; IRDA_DEBUG(0, "%s()\n", __func__ ); @@ -141,8 +140,6 @@ static void irlan_provider_connect_indication(void *instance, void *sap, IRDA_ASSERT(tsap == self->provider.tsap_ctrl,return;); IRDA_ASSERT(self->provider.state == IRLAN_IDLE, return;); - daddr = irttp_get_daddr(tsap); - saddr = irttp_get_saddr(tsap); self->provider.max_sdu_size = max_sdu_size; self->provider.max_header_size = max_header_size; diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index 986b2a5e876..e2013e434d0 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -190,7 +190,6 @@ static int afiucv_pm_freeze(struct device *dev) */ static int afiucv_pm_restore_thaw(struct device *dev) { - struct iucv_sock *iucv; struct sock *sk; struct hlist_node *node; @@ -199,7 +198,6 @@ static int afiucv_pm_restore_thaw(struct device *dev) #endif read_lock(&iucv_sk_list.lock); sk_for_each(sk, node, &iucv_sk_list.head) { - iucv = iucv_sk(sk); switch (sk->sk_state) { case IUCV_CONNECTED: sk->sk_err = EPIPE; @@ -381,7 +379,6 @@ static void iucv_sock_close(struct sock *sk) { unsigned char user_data[16]; struct iucv_sock *iucv = iucv_sk(sk); - int err; unsigned long timeo; iucv_sock_clear_timer(sk); @@ -394,8 +391,6 @@ static void iucv_sock_close(struct sock *sk) case IUCV_CONNECTED: case IUCV_DISCONN: - err = 0; - sk->sk_state = IUCV_CLOSING; sk->sk_state_change(sk); @@ -404,7 +399,7 @@ static void iucv_sock_close(struct sock *sk) timeo = sk->sk_lingertime; else timeo = IUCV_DISCONN_TIMEOUT; - err = iucv_sock_wait(sk, + iucv_sock_wait(sk, iucv_sock_in_state(sk, IUCV_CLOSED, 0), timeo); } @@ -417,7 +412,7 @@ static void iucv_sock_close(struct sock *sk) low_nmcpy(user_data, iucv->src_name); high_nmcpy(user_data, iucv->dst_name); ASCEBC(user_data, sizeof(user_data)); - err = iucv_path_sever(iucv->path, user_data); + iucv_path_sever(iucv->path, user_data); iucv_path_free(iucv->path); iucv->path = NULL; } diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c index 8f156bd86be..7f9124914b1 100644 --- a/net/iucv/iucv.c +++ b/net/iucv/iucv.c @@ -54,7 +54,7 @@ #include <asm/atomic.h> #include <asm/ebcdic.h> #include <asm/io.h> -#include <asm/s390_ext.h> +#include <asm/irq.h> #include <asm/smp.h> /* @@ -128,8 +128,8 @@ struct iucv_irq_list { }; static struct iucv_irq_data *iucv_irq_data[NR_CPUS]; -static cpumask_t iucv_buffer_cpumask = CPU_MASK_NONE; -static cpumask_t iucv_irq_cpumask = CPU_MASK_NONE; +static cpumask_t iucv_buffer_cpumask = { CPU_BITS_NONE }; +static cpumask_t iucv_irq_cpumask = { CPU_BITS_NONE }; /* * Queue of interrupt buffers lock for delivery via the tasklet @@ -406,7 +406,7 @@ static void iucv_allow_cpu(void *data) parm->set_mask.ipmask = 0xf8; iucv_call_b2f0(IUCV_SETCONTROLMASK, parm); /* Set indication that iucv interrupts are allowed for this cpu. */ - cpu_set(cpu, iucv_irq_cpumask); + cpumask_set_cpu(cpu, &iucv_irq_cpumask); } /** @@ -426,7 +426,7 @@ static void iucv_block_cpu(void *data) iucv_call_b2f0(IUCV_SETMASK, parm); /* Clear indication that iucv interrupts are allowed for this cpu. */ - cpu_clear(cpu, iucv_irq_cpumask); + cpumask_clear_cpu(cpu, &iucv_irq_cpumask); } /** @@ -451,7 +451,7 @@ static void iucv_block_cpu_almost(void *data) iucv_call_b2f0(IUCV_SETCONTROLMASK, parm); /* Clear indication that iucv interrupts are allowed for this cpu. */ - cpu_clear(cpu, iucv_irq_cpumask); + cpumask_clear_cpu(cpu, &iucv_irq_cpumask); } /** @@ -466,7 +466,7 @@ static void iucv_declare_cpu(void *data) union iucv_param *parm; int rc; - if (cpu_isset(cpu, iucv_buffer_cpumask)) + if (cpumask_test_cpu(cpu, &iucv_buffer_cpumask)) return; /* Declare interrupt buffer. */ @@ -499,9 +499,9 @@ static void iucv_declare_cpu(void *data) } /* Set indication that an iucv buffer exists for this cpu. */ - cpu_set(cpu, iucv_buffer_cpumask); + cpumask_set_cpu(cpu, &iucv_buffer_cpumask); - if (iucv_nonsmp_handler == 0 || cpus_empty(iucv_irq_cpumask)) + if (iucv_nonsmp_handler == 0 || cpumask_empty(&iucv_irq_cpumask)) /* Enable iucv interrupts on this cpu. */ iucv_allow_cpu(NULL); else @@ -520,7 +520,7 @@ static void iucv_retrieve_cpu(void *data) int cpu = smp_processor_id(); union iucv_param *parm; - if (!cpu_isset(cpu, iucv_buffer_cpumask)) + if (!cpumask_test_cpu(cpu, &iucv_buffer_cpumask)) return; /* Block iucv interrupts. */ @@ -531,7 +531,7 @@ static void iucv_retrieve_cpu(void *data) iucv_call_b2f0(IUCV_RETRIEVE_BUFFER, parm); /* Clear indication that an iucv buffer exists for this cpu. */ - cpu_clear(cpu, iucv_buffer_cpumask); + cpumask_clear_cpu(cpu, &iucv_buffer_cpumask); } /** @@ -546,8 +546,8 @@ static void iucv_setmask_mp(void) get_online_cpus(); for_each_online_cpu(cpu) /* Enable all cpus with a declared buffer. */ - if (cpu_isset(cpu, iucv_buffer_cpumask) && - !cpu_isset(cpu, iucv_irq_cpumask)) + if (cpumask_test_cpu(cpu, &iucv_buffer_cpumask) && + !cpumask_test_cpu(cpu, &iucv_irq_cpumask)) smp_call_function_single(cpu, iucv_allow_cpu, NULL, 1); put_online_cpus(); @@ -564,9 +564,9 @@ static void iucv_setmask_up(void) int cpu; /* Disable all cpu but the first in cpu_irq_cpumask. */ - cpumask = iucv_irq_cpumask; - cpu_clear(first_cpu(iucv_irq_cpumask), cpumask); - for_each_cpu_mask_nr(cpu, cpumask) + cpumask_copy(&cpumask, &iucv_irq_cpumask); + cpumask_clear_cpu(cpumask_first(&iucv_irq_cpumask), &cpumask); + for_each_cpu(cpu, &cpumask) smp_call_function_single(cpu, iucv_block_cpu, NULL, 1); } @@ -593,7 +593,7 @@ static int iucv_enable(void) rc = -EIO; for_each_online_cpu(cpu) smp_call_function_single(cpu, iucv_declare_cpu, NULL, 1); - if (cpus_empty(iucv_buffer_cpumask)) + if (cpumask_empty(&iucv_buffer_cpumask)) /* No cpu could declare an iucv buffer. */ goto out; put_online_cpus(); @@ -675,15 +675,16 @@ static int __cpuinit iucv_cpu_notify(struct notifier_block *self, case CPU_DOWN_PREPARE_FROZEN: if (!iucv_path_table) break; - cpumask = iucv_buffer_cpumask; - cpu_clear(cpu, cpumask); - if (cpus_empty(cpumask)) + cpumask_copy(&cpumask, &iucv_buffer_cpumask); + cpumask_clear_cpu(cpu, &cpumask); + if (cpumask_empty(&cpumask)) /* Can't offline last IUCV enabled cpu. */ return notifier_from_errno(-EINVAL); smp_call_function_single(cpu, iucv_retrieve_cpu, NULL, 1); - if (cpus_empty(iucv_irq_cpumask)) - smp_call_function_single(first_cpu(iucv_buffer_cpumask), - iucv_allow_cpu, NULL, 1); + if (cpumask_empty(&iucv_irq_cpumask)) + smp_call_function_single( + cpumask_first(&iucv_buffer_cpumask), + iucv_allow_cpu, NULL, 1); break; } return NOTIFY_OK; @@ -828,14 +829,14 @@ EXPORT_SYMBOL(iucv_unregister); static int iucv_reboot_event(struct notifier_block *this, unsigned long event, void *ptr) { - int i, rc; + int i; get_online_cpus(); on_each_cpu(iucv_block_cpu, NULL, 1); preempt_disable(); for (i = 0; i < iucv_max_pathid; i++) { if (iucv_path_table[i]) - rc = iucv_sever_pathid(i, NULL); + iucv_sever_pathid(i, NULL); } preempt_enable(); put_online_cpus(); @@ -866,7 +867,7 @@ int iucv_path_accept(struct iucv_path *path, struct iucv_handler *handler, int rc; local_bh_disable(); - if (cpus_empty(iucv_buffer_cpumask)) { + if (cpumask_empty(&iucv_buffer_cpumask)) { rc = -EIO; goto out; } @@ -915,7 +916,7 @@ int iucv_path_connect(struct iucv_path *path, struct iucv_handler *handler, spin_lock_bh(&iucv_table_lock); iucv_cleanup_queue(); - if (cpus_empty(iucv_buffer_cpumask)) { + if (cpumask_empty(&iucv_buffer_cpumask)) { rc = -EIO; goto out; } @@ -975,7 +976,7 @@ int iucv_path_quiesce(struct iucv_path *path, u8 userdata[16]) int rc; local_bh_disable(); - if (cpus_empty(iucv_buffer_cpumask)) { + if (cpumask_empty(&iucv_buffer_cpumask)) { rc = -EIO; goto out; } @@ -1007,7 +1008,7 @@ int iucv_path_resume(struct iucv_path *path, u8 userdata[16]) int rc; local_bh_disable(); - if (cpus_empty(iucv_buffer_cpumask)) { + if (cpumask_empty(&iucv_buffer_cpumask)) { rc = -EIO; goto out; } @@ -1036,7 +1037,7 @@ int iucv_path_sever(struct iucv_path *path, u8 userdata[16]) int rc; preempt_disable(); - if (cpus_empty(iucv_buffer_cpumask)) { + if (cpumask_empty(&iucv_buffer_cpumask)) { rc = -EIO; goto out; } @@ -1070,7 +1071,7 @@ int iucv_message_purge(struct iucv_path *path, struct iucv_message *msg, int rc; local_bh_disable(); - if (cpus_empty(iucv_buffer_cpumask)) { + if (cpumask_empty(&iucv_buffer_cpumask)) { rc = -EIO; goto out; } @@ -1162,7 +1163,7 @@ int __iucv_message_receive(struct iucv_path *path, struct iucv_message *msg, if (msg->flags & IUCV_IPRMDATA) return iucv_message_receive_iprmdata(path, msg, flags, buffer, size, residual); - if (cpus_empty(iucv_buffer_cpumask)) { + if (cpumask_empty(&iucv_buffer_cpumask)) { rc = -EIO; goto out; } @@ -1235,7 +1236,7 @@ int iucv_message_reject(struct iucv_path *path, struct iucv_message *msg) int rc; local_bh_disable(); - if (cpus_empty(iucv_buffer_cpumask)) { + if (cpumask_empty(&iucv_buffer_cpumask)) { rc = -EIO; goto out; } @@ -1274,7 +1275,7 @@ int iucv_message_reply(struct iucv_path *path, struct iucv_message *msg, int rc; local_bh_disable(); - if (cpus_empty(iucv_buffer_cpumask)) { + if (cpumask_empty(&iucv_buffer_cpumask)) { rc = -EIO; goto out; } @@ -1324,7 +1325,7 @@ int __iucv_message_send(struct iucv_path *path, struct iucv_message *msg, union iucv_param *parm; int rc; - if (cpus_empty(iucv_buffer_cpumask)) { + if (cpumask_empty(&iucv_buffer_cpumask)) { rc = -EIO; goto out; } @@ -1411,7 +1412,7 @@ int iucv_message_send2way(struct iucv_path *path, struct iucv_message *msg, int rc; local_bh_disable(); - if (cpus_empty(iucv_buffer_cpumask)) { + if (cpumask_empty(&iucv_buffer_cpumask)) { rc = -EIO; goto out; } @@ -1888,7 +1889,7 @@ static int iucv_pm_freeze(struct device *dev) printk(KERN_WARNING "iucv_pm_freeze\n"); #endif if (iucv_pm_state != IUCV_PM_FREEZING) { - for_each_cpu_mask_nr(cpu, iucv_irq_cpumask) + for_each_cpu(cpu, &iucv_irq_cpumask) smp_call_function_single(cpu, iucv_block_cpu_almost, NULL, 1); cancel_work_sync(&iucv_work); @@ -1928,7 +1929,7 @@ static int iucv_pm_thaw(struct device *dev) if (rc) goto out; } - if (cpus_empty(iucv_irq_cpumask)) { + if (cpumask_empty(&iucv_irq_cpumask)) { if (iucv_nonsmp_handler) /* enable interrupts on one cpu */ iucv_allow_cpu(NULL); @@ -1961,7 +1962,7 @@ static int iucv_pm_restore(struct device *dev) pr_warning("Suspending Linux did not completely close all IUCV " "connections\n"); iucv_pm_state = IUCV_PM_RESTORING; - if (cpus_empty(iucv_irq_cpumask)) { + if (cpumask_empty(&iucv_irq_cpumask)) { rc = iucv_query_maxconn(); rc = iucv_enable(); if (rc) diff --git a/net/key/af_key.c b/net/key/af_key.c index 7db86ffcf07..8f92cf8116e 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -712,7 +712,7 @@ static unsigned int pfkey_sockaddr_fill(const xfrm_address_t *xaddr, __be16 port sin6->sin6_family = AF_INET6; sin6->sin6_port = port; sin6->sin6_flowinfo = 0; - ipv6_addr_copy(&sin6->sin6_addr, (struct in6_addr *)xaddr->a6); + ipv6_addr_copy(&sin6->sin6_addr, (const struct in6_addr *)xaddr->a6); sin6->sin6_scope_id = 0; return 128; } @@ -3656,7 +3656,7 @@ static int pfkey_seq_show(struct seq_file *f, void *v) if (v == SEQ_START_TOKEN) seq_printf(f ,"sk RefCnt Rmem Wmem User Inode\n"); else - seq_printf(f ,"%p %-6d %-6u %-6u %-6u %-6lu\n", + seq_printf(f, "%pK %-6d %-6u %-6u %-6u %-6lu\n", s, atomic_read(&s->sk_refcnt), sk_rmem_alloc_get(s), diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index c64ce0a0bb0..ed8a2335442 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -954,7 +954,7 @@ static int l2tp_build_l2tpv3_header(struct l2tp_session *session, void *buf) } static int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb, - size_t data_len) + struct flowi *fl, size_t data_len) { struct l2tp_tunnel *tunnel = session->tunnel; unsigned int len = skb->len; @@ -987,7 +987,7 @@ static int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb, /* Queue the packet to IP for output */ skb->local_df = 1; - error = ip_queue_xmit(skb); + error = ip_queue_xmit(skb, fl); /* Update stats */ if (error >= 0) { @@ -1028,6 +1028,7 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len int data_len = skb->len; struct l2tp_tunnel *tunnel = session->tunnel; struct sock *sk = tunnel->sock; + struct flowi *fl; struct udphdr *uh; struct inet_sock *inet; __wsum csum; @@ -1060,14 +1061,21 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len IPSKB_REROUTED); nf_reset(skb); + bh_lock_sock(sk); + if (sock_owned_by_user(sk)) { + dev_kfree_skb(skb); + goto out_unlock; + } + /* Get routing info from the tunnel socket */ skb_dst_drop(skb); skb_dst_set(skb, dst_clone(__sk_dst_get(sk))); + inet = inet_sk(sk); + fl = &inet->cork.fl; switch (tunnel->encap) { case L2TP_ENCAPTYPE_UDP: /* Setup UDP header */ - inet = inet_sk(sk); __skb_push(skb, sizeof(*uh)); skb_reset_transport_header(skb); uh = udp_hdr(skb); @@ -1105,7 +1113,9 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len l2tp_skb_set_owner_w(skb, sk); - l2tp_xmit_core(session, skb, data_len); + l2tp_xmit_core(session, skb, fl, data_len); +out_unlock: + bh_unlock_sock(sk); abort: return 0; @@ -1425,16 +1435,15 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 /* Add tunnel to our list */ INIT_LIST_HEAD(&tunnel->list); - spin_lock_bh(&pn->l2tp_tunnel_list_lock); - list_add_rcu(&tunnel->list, &pn->l2tp_tunnel_list); - spin_unlock_bh(&pn->l2tp_tunnel_list_lock); - synchronize_rcu(); atomic_inc(&l2tp_tunnel_count); /* Bump the reference count. The tunnel context is deleted - * only when this drops to zero. + * only when this drops to zero. Must be done before list insertion */ l2tp_tunnel_inc_refcount(tunnel); + spin_lock_bh(&pn->l2tp_tunnel_list_lock); + list_add_rcu(&tunnel->list, &pn->l2tp_tunnel_list); + spin_unlock_bh(&pn->l2tp_tunnel_list_lock); err = 0; err: @@ -1626,7 +1635,6 @@ struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunn hlist_add_head_rcu(&session->global_hlist, l2tp_session_id_hash_2(pn, session_id)); spin_unlock_bh(&pn->l2tp_session_hlist_lock); - synchronize_rcu(); } /* Ignore management session in session count value */ diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c index fce9bd3bd3f..d21e7ebd91c 100644 --- a/net/l2tp/l2tp_ip.c +++ b/net/l2tp/l2tp_ip.c @@ -296,12 +296,12 @@ out_in_use: static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) { - int rc; - struct inet_sock *inet = inet_sk(sk); struct sockaddr_l2tpip *lsa = (struct sockaddr_l2tpip *) uaddr; + struct inet_sock *inet = inet_sk(sk); + struct flowi4 *fl4; struct rtable *rt; __be32 saddr; - int oif; + int oif, rc; rc = -EINVAL; if (addr_len < sizeof(*lsa)) @@ -311,6 +311,8 @@ static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len if (lsa->l2tp_family != AF_INET) goto out; + lock_sock(sk); + sk_dst_reset(sk); oif = sk->sk_bound_dev_if; @@ -320,7 +322,8 @@ static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len if (ipv4_is_multicast(lsa->l2tp_addr.s_addr)) goto out; - rt = ip_route_connect(lsa->l2tp_addr.s_addr, saddr, + fl4 = &inet->cork.fl.u.ip4; + rt = ip_route_connect(fl4, lsa->l2tp_addr.s_addr, saddr, RT_CONN_FLAGS(sk), oif, IPPROTO_L2TP, 0, 0, sk, true); @@ -340,10 +343,10 @@ static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len l2tp_ip_sk(sk)->peer_conn_id = lsa->l2tp_conn_id; if (!inet->inet_saddr) - inet->inet_saddr = rt->rt_src; + inet->inet_saddr = fl4->saddr; if (!inet->inet_rcv_saddr) - inet->inet_rcv_saddr = rt->rt_src; - inet->inet_daddr = rt->rt_dst; + inet->inet_rcv_saddr = fl4->saddr; + inet->inet_daddr = fl4->daddr; sk->sk_state = TCP_ESTABLISHED; inet->inet_id = jiffies; @@ -356,6 +359,7 @@ static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len rc = 0; out: + release_sock(sk); return rc; } @@ -416,23 +420,28 @@ static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m int rc; struct l2tp_ip_sock *lsa = l2tp_ip_sk(sk); struct inet_sock *inet = inet_sk(sk); - struct ip_options *opt = inet->opt; struct rtable *rt = NULL; + struct flowi4 *fl4; int connected = 0; __be32 daddr; + lock_sock(sk); + + rc = -ENOTCONN; if (sock_flag(sk, SOCK_DEAD)) - return -ENOTCONN; + goto out; /* Get and verify the address. */ if (msg->msg_name) { struct sockaddr_l2tpip *lip = (struct sockaddr_l2tpip *) msg->msg_name; + rc = -EINVAL; if (msg->msg_namelen < sizeof(*lip)) - return -EINVAL; + goto out; if (lip->l2tp_family != AF_INET) { + rc = -EAFNOSUPPORT; if (lip->l2tp_family != AF_UNSPEC) - return -EAFNOSUPPORT; + goto out; } daddr = lip->l2tp_addr.s_addr; @@ -467,31 +476,45 @@ static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m goto error; } + fl4 = &inet->cork.fl.u.ip4; if (connected) rt = (struct rtable *) __sk_dst_check(sk, 0); + rcu_read_lock(); if (rt == NULL) { + const struct ip_options_rcu *inet_opt; + + inet_opt = rcu_dereference(inet->inet_opt); + /* Use correct destination address if we have options. */ - if (opt && opt->srr) - daddr = opt->faddr; + if (inet_opt && inet_opt->opt.srr) + daddr = inet_opt->opt.faddr; /* If this fails, retransmit mechanism of transport layer will * keep trying until route appears or the connection times * itself out. */ - rt = ip_route_output_ports(sock_net(sk), sk, + rt = ip_route_output_ports(sock_net(sk), fl4, sk, daddr, inet->inet_saddr, inet->inet_dport, inet->inet_sport, sk->sk_protocol, RT_CONN_FLAGS(sk), sk->sk_bound_dev_if); if (IS_ERR(rt)) goto no_route; - sk_setup_caps(sk, &rt->dst); + if (connected) + sk_setup_caps(sk, &rt->dst); + else + dst_release(&rt->dst); /* safe since we hold rcu_read_lock */ } - skb_dst_set(skb, dst_clone(&rt->dst)); + + /* We dont need to clone dst here, it is guaranteed to not disappear. + * __dev_xmit_skb() might force a refcount if needed. + */ + skb_dst_set_noref(skb, &rt->dst); /* Queue the packet to IP for output */ - rc = ip_queue_xmit(skb); + rc = ip_queue_xmit(skb, &inet->cork.fl); + rcu_read_unlock(); error: /* Update stats */ @@ -503,12 +526,16 @@ error: lsa->tx_errors++; } +out: + release_sock(sk); return rc; no_route: + rcu_read_unlock(); IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES); kfree_skb(skb); - return -EHOSTUNREACH; + rc = -EHOSTUNREACH; + goto out; } static int l2tp_ip_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, @@ -667,7 +694,7 @@ MODULE_AUTHOR("James Chapman <jchapman@katalix.com>"); MODULE_DESCRIPTION("L2TP over IP"); MODULE_VERSION("1.0"); -/* Use the value of SOCK_DGRAM (2) directory, because __stringify does't like +/* Use the value of SOCK_DGRAM (2) directory, because __stringify doesn't like * enums */ MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET, 2, IPPROTO_L2TP); diff --git a/net/mac80211/aes_ccm.c b/net/mac80211/aes_ccm.c index 4bd6ef0be38..b9b595c0811 100644 --- a/net/mac80211/aes_ccm.c +++ b/net/mac80211/aes_ccm.c @@ -54,13 +54,12 @@ void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch, u8 *cdata, u8 *mic) { int i, j, last_len, num_blocks; - u8 *pos, *cpos, *b, *s_0, *e, *b_0, *aad; + u8 *pos, *cpos, *b, *s_0, *e, *b_0; b = scratch; s_0 = scratch + AES_BLOCK_LEN; e = scratch + 2 * AES_BLOCK_LEN; b_0 = scratch + 3 * AES_BLOCK_LEN; - aad = scratch + 4 * AES_BLOCK_LEN; num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN); last_len = data_len % AES_BLOCK_LEN; @@ -94,13 +93,12 @@ int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch, u8 *cdata, size_t data_len, u8 *mic, u8 *data) { int i, j, last_len, num_blocks; - u8 *pos, *cpos, *b, *s_0, *a, *b_0, *aad; + u8 *pos, *cpos, *b, *s_0, *a, *b_0; b = scratch; s_0 = scratch + AES_BLOCK_LEN; a = scratch + 2 * AES_BLOCK_LEN; b_0 = scratch + 3 * AES_BLOCK_LEN; - aad = scratch + 4 * AES_BLOCK_LEN; num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN); last_len = data_len % AES_BLOCK_LEN; diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 0c9d0c07eae..89b0b2ca6db 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -63,7 +63,8 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, lockdep_assert_held(&sta->ampdu_mlme.mtx); - tid_rx = sta->ampdu_mlme.tid_rx[tid]; + tid_rx = rcu_dereference_protected(sta->ampdu_mlme.tid_rx[tid], + lockdep_is_held(&sta->ampdu_mlme.mtx)); if (!tid_rx) return; @@ -99,6 +100,21 @@ void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, mutex_unlock(&sta->ampdu_mlme.mtx); } +void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap, + const u8 *addr) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct sta_info *sta = sta_info_get(sdata, addr); + int i; + + for (i = 0; i < STA_TID_NUM; i++) + if (ba_rx_bitmap & BIT(i)) + set_bit(i, sta->ampdu_mlme.tid_rx_stop_requested); + + ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work); +} +EXPORT_SYMBOL(ieee80211_stop_rx_ba_session); + /* * After accepting the AddBA Request we activated a timer, * resetting it after each frame that arrives from the originator. diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 63d852cb4ca..c8be8eff70d 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -136,12 +136,12 @@ void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u1 ieee80211_tx_skb(sdata, skb); } -static void kfree_tid_tx(struct rcu_head *rcu_head) +void ieee80211_assign_tid_tx(struct sta_info *sta, int tid, + struct tid_ampdu_tx *tid_tx) { - struct tid_ampdu_tx *tid_tx = - container_of(rcu_head, struct tid_ampdu_tx, rcu_head); - - kfree(tid_tx); + lockdep_assert_held(&sta->ampdu_mlme.mtx); + lockdep_assert_held(&sta->lock); + rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], tid_tx); } int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, @@ -149,21 +149,24 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, bool tx) { struct ieee80211_local *local = sta->local; - struct tid_ampdu_tx *tid_tx = sta->ampdu_mlme.tid_tx[tid]; + struct tid_ampdu_tx *tid_tx; int ret; lockdep_assert_held(&sta->ampdu_mlme.mtx); - if (!tid_tx) - return -ENOENT; - spin_lock_bh(&sta->lock); + tid_tx = rcu_dereference_protected_tid_tx(sta, tid); + if (!tid_tx) { + spin_unlock_bh(&sta->lock); + return -ENOENT; + } + if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) { /* not even started yet! */ - rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], NULL); + ieee80211_assign_tid_tx(sta, tid, NULL); spin_unlock_bh(&sta->lock); - call_rcu(&tid_tx->rcu_head, kfree_tid_tx); + kfree_rcu(tid_tx, rcu_head); return 0; } @@ -283,13 +286,13 @@ ieee80211_wake_queue_agg(struct ieee80211_local *local, int tid) void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) { - struct tid_ampdu_tx *tid_tx = sta->ampdu_mlme.tid_tx[tid]; + struct tid_ampdu_tx *tid_tx; struct ieee80211_local *local = sta->local; struct ieee80211_sub_if_data *sdata = sta->sdata; u16 start_seq_num; int ret; - lockdep_assert_held(&sta->ampdu_mlme.mtx); + tid_tx = rcu_dereference_protected_tid_tx(sta, tid); /* * While we're asking the driver about the aggregation, @@ -318,11 +321,11 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) " tid %d\n", tid); #endif spin_lock_bh(&sta->lock); - rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], NULL); + ieee80211_assign_tid_tx(sta, tid, NULL); spin_unlock_bh(&sta->lock); ieee80211_wake_queue_agg(local, tid); - call_rcu(&tid_tx->rcu_head, kfree_tid_tx); + kfree_rcu(tid_tx, rcu_head); return; } @@ -396,9 +399,9 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, goto err_unlock_sta; } - tid_tx = sta->ampdu_mlme.tid_tx[tid]; + tid_tx = rcu_dereference_protected_tid_tx(sta, tid); /* check if the TID is not in aggregation flow already */ - if (tid_tx) { + if (tid_tx || sta->ampdu_mlme.tid_start_tx[tid]) { #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "BA request denied - session is not " "idle on tid %u\n", tid); @@ -433,8 +436,11 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, sta->ampdu_mlme.dialog_token_allocator++; tid_tx->dialog_token = sta->ampdu_mlme.dialog_token_allocator; - /* finally, assign it to the array */ - rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], tid_tx); + /* + * Finally, assign it to the start array; the work item will + * collect it and move it to the normal array. + */ + sta->ampdu_mlme.tid_start_tx[tid] = tid_tx; ieee80211_queue_work(&local->hw, &sta->ampdu_mlme.work); @@ -480,16 +486,19 @@ ieee80211_agg_splice_finish(struct ieee80211_local *local, u16 tid) static void ieee80211_agg_tx_operational(struct ieee80211_local *local, struct sta_info *sta, u16 tid) { + struct tid_ampdu_tx *tid_tx; + lockdep_assert_held(&sta->ampdu_mlme.mtx); + tid_tx = rcu_dereference_protected_tid_tx(sta, tid); + #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "Aggregation is on for tid %d\n", tid); #endif drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_TX_OPERATIONAL, - &sta->sta, tid, NULL, - sta->ampdu_mlme.tid_tx[tid]->buf_size); + &sta->sta, tid, NULL, tid_tx->buf_size); /* * synchronize with TX path, while splicing the TX path @@ -497,13 +506,13 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local, */ spin_lock_bh(&sta->lock); - ieee80211_agg_splice_packets(local, sta->ampdu_mlme.tid_tx[tid], tid); + ieee80211_agg_splice_packets(local, tid_tx, tid); /* * Now mark as operational. This will be visible * in the TX path, and lets it go lock-free in * the common case. */ - set_bit(HT_AGG_STATE_OPERATIONAL, &sta->ampdu_mlme.tid_tx[tid]->state); + set_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state); ieee80211_agg_splice_finish(local, tid); spin_unlock_bh(&sta->lock); @@ -537,7 +546,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid) } mutex_lock(&sta->ampdu_mlme.mtx); - tid_tx = sta->ampdu_mlme.tid_tx[tid]; + tid_tx = rcu_dereference_protected_tid_tx(sta, tid); if (WARN_ON(!tid_tx)) { #ifdef CONFIG_MAC80211_HT_DEBUG @@ -615,7 +624,7 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) return -EINVAL; spin_lock_bh(&sta->lock); - tid_tx = sta->ampdu_mlme.tid_tx[tid]; + tid_tx = rcu_dereference_protected_tid_tx(sta, tid); if (!tid_tx) { ret = -ENOENT; @@ -671,7 +680,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid) mutex_lock(&sta->ampdu_mlme.mtx); spin_lock_bh(&sta->lock); - tid_tx = sta->ampdu_mlme.tid_tx[tid]; + tid_tx = rcu_dereference_protected_tid_tx(sta, tid); if (!tid_tx || !test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) { #ifdef CONFIG_MAC80211_HT_DEBUG @@ -697,11 +706,11 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid) ieee80211_agg_splice_packets(local, tid_tx, tid); /* future packets must not find the tid_tx struct any more */ - rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], NULL); + ieee80211_assign_tid_tx(sta, tid, NULL); ieee80211_agg_splice_finish(local, tid); - call_rcu(&tid_tx->rcu_head, kfree_tid_tx); + kfree_rcu(tid_tx, rcu_head); unlock_sta: spin_unlock_bh(&sta->lock); @@ -752,7 +761,7 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, mutex_lock(&sta->ampdu_mlme.mtx); - tid_tx = sta->ampdu_mlme.tid_tx[tid]; + tid_tx = rcu_dereference_protected_tid_tx(sta, tid); if (!tid_tx) goto out; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index bf5d28da46e..6e56c6ee7cc 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -136,7 +136,10 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, mutex_lock(&sdata->local->sta_mtx); if (mac_addr) { - sta = sta_info_get_bss(sdata, mac_addr); + if (ieee80211_vif_is_mesh(&sdata->vif)) + sta = sta_info_get(sdata, mac_addr); + else + sta = sta_info_get_bss(sdata, mac_addr); if (!sta) { ieee80211_key_free(sdata->local, key); err = -ENOENT; @@ -157,13 +160,14 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev, u8 key_idx, bool pairwise, const u8 *mac_addr) { - struct ieee80211_sub_if_data *sdata; + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; struct sta_info *sta; + struct ieee80211_key *key = NULL; int ret; - sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - mutex_lock(&sdata->local->sta_mtx); + mutex_lock(&local->sta_mtx); + mutex_lock(&local->key_mtx); if (mac_addr) { ret = -ENOENT; @@ -172,33 +176,24 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev, if (!sta) goto out_unlock; - if (pairwise) { - if (sta->ptk) { - ieee80211_key_free(sdata->local, sta->ptk); - ret = 0; - } - } else { - if (sta->gtk[key_idx]) { - ieee80211_key_free(sdata->local, - sta->gtk[key_idx]); - ret = 0; - } - } - - goto out_unlock; - } + if (pairwise) + key = key_mtx_dereference(local, sta->ptk); + else + key = key_mtx_dereference(local, sta->gtk[key_idx]); + } else + key = key_mtx_dereference(local, sdata->keys[key_idx]); - if (!sdata->keys[key_idx]) { + if (!key) { ret = -ENOENT; goto out_unlock; } - ieee80211_key_free(sdata->local, sdata->keys[key_idx]); - WARN_ON(sdata->keys[key_idx]); + __ieee80211_key_free(key); ret = 0; out_unlock: - mutex_unlock(&sdata->local->sta_mtx); + mutex_unlock(&local->key_mtx); + mutex_unlock(&local->sta_mtx); return ret; } @@ -228,11 +223,11 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, goto out; if (pairwise) - key = sta->ptk; + key = rcu_dereference(sta->ptk); else if (key_idx < NUM_DEFAULT_KEYS) - key = sta->gtk[key_idx]; + key = rcu_dereference(sta->gtk[key_idx]); } else - key = sdata->keys[key_idx]; + key = rcu_dereference(sdata->keys[key_idx]); if (!key) goto out; @@ -330,6 +325,7 @@ static void rate_idx_to_bitrate(struct rate_info *rate, struct sta_info *sta, in static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) { struct ieee80211_sub_if_data *sdata = sta->sdata; + struct timespec uptime; sinfo->generation = sdata->local->sta_generation; @@ -343,7 +339,11 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) STATION_INFO_TX_BITRATE | STATION_INFO_RX_BITRATE | STATION_INFO_RX_DROP_MISC | - STATION_INFO_BSS_PARAM; + STATION_INFO_BSS_PARAM | + STATION_INFO_CONNECTED_TIME; + + do_posix_clock_monotonic_gettime(&uptime); + sinfo->connected_time = uptime.tv_sec - sta->last_connected; sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx); sinfo->rx_bytes = sta->rx_bytes; @@ -463,7 +463,7 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata, int size; int err = -EINVAL; - old = sdata->u.ap.beacon; + old = rtnl_dereference(sdata->u.ap.beacon); /* head must not be zero-length */ if (params->head && !params->head_len) @@ -558,8 +558,7 @@ static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); - old = sdata->u.ap.beacon; - + old = rtnl_dereference(sdata->u.ap.beacon); if (old) return -EALREADY; @@ -574,8 +573,7 @@ static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); - old = sdata->u.ap.beacon; - + old = rtnl_dereference(sdata->u.ap.beacon); if (!old) return -ENOENT; @@ -589,8 +587,7 @@ static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev) sdata = IEEE80211_DEV_TO_SUB_IF(dev); - old = sdata->u.ap.beacon; - + old = rtnl_dereference(sdata->u.ap.beacon); if (!old) return -ENOENT; @@ -686,6 +683,12 @@ static void sta_apply_parameters(struct ieee80211_local *local, if (set & BIT(NL80211_STA_FLAG_MFP)) sta->flags |= WLAN_STA_MFP; } + + if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) { + sta->flags &= ~WLAN_STA_AUTH; + if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) + sta->flags |= WLAN_STA_AUTH; + } spin_unlock_irqrestore(&sta->flaglock, flags); /* @@ -723,15 +726,29 @@ static void sta_apply_parameters(struct ieee80211_local *local, params->ht_capa, &sta->sta.ht_cap); - if (ieee80211_vif_is_mesh(&sdata->vif) && params->plink_action) { - switch (params->plink_action) { - case PLINK_ACTION_OPEN: - mesh_plink_open(sta); - break; - case PLINK_ACTION_BLOCK: - mesh_plink_block(sta); - break; - } + if (ieee80211_vif_is_mesh(&sdata->vif)) { +#ifdef CONFIG_MAC80211_MESH + if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED) + switch (params->plink_state) { + case NL80211_PLINK_LISTEN: + case NL80211_PLINK_ESTAB: + case NL80211_PLINK_BLOCKED: + sta->plink_state = params->plink_state; + break; + default: + /* nothing */ + break; + } + else + switch (params->plink_action) { + case PLINK_ACTION_OPEN: + mesh_plink_open(sta); + break; + case PLINK_ACTION_BLOCK: + mesh_plink_block(sta); + break; + } +#endif } } @@ -932,8 +949,10 @@ static int ieee80211_change_mpath(struct wiphy *wiphy, static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop, struct mpath_info *pinfo) { - if (mpath->next_hop) - memcpy(next_hop, mpath->next_hop->sta.addr, ETH_ALEN); + struct sta_info *next_hop_sta = rcu_dereference(mpath->next_hop); + + if (next_hop_sta) + memcpy(next_hop, next_hop_sta->sta.addr, ETH_ALEN); else memset(next_hop, 0, ETH_ALEN); @@ -1034,26 +1053,30 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh, u8 *new_ie; const u8 *old_ie; - /* first allocate the new vendor information element */ + /* allocate information elements */ new_ie = NULL; - old_ie = ifmsh->vendor_ie; + old_ie = ifmsh->ie; - ifmsh->vendor_ie_len = setup->vendor_ie_len; - if (setup->vendor_ie_len) { - new_ie = kmemdup(setup->vendor_ie, setup->vendor_ie_len, + if (setup->ie_len) { + new_ie = kmemdup(setup->ie, setup->ie_len, GFP_KERNEL); if (!new_ie) return -ENOMEM; } + ifmsh->ie_len = setup->ie_len; + ifmsh->ie = new_ie; + kfree(old_ie); /* now copy the rest of the setup parameters */ ifmsh->mesh_id_len = setup->mesh_id_len; memcpy(ifmsh->mesh_id, setup->mesh_id, ifmsh->mesh_id_len); ifmsh->mesh_pp_id = setup->path_sel_proto; ifmsh->mesh_pm_id = setup->path_metric; - ifmsh->vendor_ie = new_ie; - - kfree(old_ie); + ifmsh->security = IEEE80211_MESH_SEC_NONE; + if (setup->is_authenticated) + ifmsh->security |= IEEE80211_MESH_SEC_AUTHED; + if (setup->is_secure) + ifmsh->security |= IEEE80211_MESH_SEC_SECURED; return 0; } @@ -1286,9 +1309,10 @@ static int ieee80211_set_channel(struct wiphy *wiphy, } #ifdef CONFIG_PM -static int ieee80211_suspend(struct wiphy *wiphy) +static int ieee80211_suspend(struct wiphy *wiphy, + struct cfg80211_wowlan *wowlan) { - return __ieee80211_suspend(wiphy_priv(wiphy)); + return __ieee80211_suspend(wiphy_priv(wiphy), wowlan); } static int ieee80211_resume(struct wiphy *wiphy) @@ -1331,6 +1355,30 @@ static int ieee80211_scan(struct wiphy *wiphy, return ieee80211_request_scan(sdata, req); } +static int +ieee80211_sched_scan_start(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_sched_scan_request *req) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (!sdata->local->ops->sched_scan_start) + return -EOPNOTSUPP; + + return ieee80211_request_sched_scan_start(sdata, req); +} + +static int +ieee80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (!sdata->local->ops->sched_scan_stop) + return -EOPNOTSUPP; + + return ieee80211_request_sched_scan_stop(sdata); +} + static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_auth_request *req) { @@ -1506,6 +1554,19 @@ static int ieee80211_testmode_cmd(struct wiphy *wiphy, void *data, int len) return local->ops->testmode_cmd(&local->hw, data, len); } + +static int ieee80211_testmode_dump(struct wiphy *wiphy, + struct sk_buff *skb, + struct netlink_callback *cb, + void *data, int len) +{ + struct ieee80211_local *local = wiphy_priv(wiphy); + + if (!local->ops->testmode_dump) + return -EOPNOTSUPP; + + return local->ops->testmode_dump(&local->hw, skb, cb, data, len); +} #endif int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, @@ -1515,6 +1576,8 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, enum ieee80211_smps_mode old_req; int err; + lockdep_assert_held(&sdata->u.mgd.mtx); + old_req = sdata->u.mgd.req_smps; sdata->u.mgd.req_smps = smps_mode; @@ -1620,16 +1683,13 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy, { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - int i; - - /* - * This _could_ be supported by providing a hook for - * drivers for this function, but at this point it - * doesn't seem worth bothering. - */ - if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) - return -EOPNOTSUPP; + int i, ret; + if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) { + ret = drv_set_bitrate_mask(local, sdata, mask); + if (ret) + return ret; + } for (i = 0; i < IEEE80211_NUM_BANDS; i++) sdata->rc_rateidx_mask[i] = mask->control[i].legacy; @@ -2073,6 +2133,8 @@ struct cfg80211_ops mac80211_config_ops = { .suspend = ieee80211_suspend, .resume = ieee80211_resume, .scan = ieee80211_scan, + .sched_scan_start = ieee80211_sched_scan_start, + .sched_scan_stop = ieee80211_sched_scan_stop, .auth = ieee80211_auth, .assoc = ieee80211_assoc, .deauth = ieee80211_deauth, @@ -2085,6 +2147,7 @@ struct cfg80211_ops mac80211_config_ops = { .set_wds_peer = ieee80211_set_wds_peer, .rfkill_poll = ieee80211_rfkill_poll, CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd) + CFG80211_TESTMODE_DUMP(ieee80211_testmode_dump) .set_power_mgmt = ieee80211_set_power_mgmt, .set_bitrate_mask = ieee80211_set_bitrate_mask, .remain_on_channel = ieee80211_remain_on_channel, diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 0a602dbfdb2..186e02f7cc3 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -135,7 +135,7 @@ static ssize_t reset_write(struct file *file, const char __user *user_buf, struct ieee80211_local *local = file->private_data; rtnl_lock(); - __ieee80211_suspend(&local->hw); + __ieee80211_suspend(&local->hw, NULL); __ieee80211_resume(&local->hw); rtnl_unlock(); diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c index f7ef3477c24..33c58b85c91 100644 --- a/net/mac80211/debugfs_key.c +++ b/net/mac80211/debugfs_key.c @@ -241,16 +241,12 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key) if (!key->debugfs.dir) return; - rcu_read_lock(); - sta = rcu_dereference(key->sta); - if (sta) + sta = key->sta; + if (sta) { sprintf(buf, "../../stations/%pM", sta->sta.addr); - rcu_read_unlock(); - - /* using sta as a boolean is fine outside RCU lock */ - if (sta) key->debugfs.stalink = debugfs_create_symlink("station", key->debugfs.dir, buf); + } DEBUGFS_ADD(keylen); DEBUGFS_ADD(flags); @@ -286,7 +282,8 @@ void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata) lockdep_assert_held(&sdata->local->key_mtx); if (sdata->default_unicast_key) { - key = sdata->default_unicast_key; + key = key_mtx_dereference(sdata->local, + sdata->default_unicast_key); sprintf(buf, "../keys/%d", key->debugfs.cnt); sdata->debugfs.default_unicast_key = debugfs_create_symlink("default_unicast_key", @@ -297,7 +294,8 @@ void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata) } if (sdata->default_multicast_key) { - key = sdata->default_multicast_key; + key = key_mtx_dereference(sdata->local, + sdata->default_multicast_key); sprintf(buf, "../keys/%d", key->debugfs.cnt); sdata->debugfs.default_multicast_key = debugfs_create_symlink("default_multicast_key", @@ -316,9 +314,8 @@ void ieee80211_debugfs_key_add_mgmt_default(struct ieee80211_sub_if_data *sdata) if (!sdata->debugfs.dir) return; - /* this is running under the key lock */ - - key = sdata->default_mgmt_key; + key = key_mtx_dereference(sdata->local, + sdata->default_mgmt_key); if (key) { sprintf(buf, "../keys/%d", key->debugfs.cnt); sdata->debugfs.default_mgmt_key = diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index dacace6b139..9ea7c0d0103 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -177,9 +177,9 @@ static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata, if (sdata->vif.type != NL80211_IFTYPE_STATION) return -EOPNOTSUPP; - mutex_lock(&local->iflist_mtx); + mutex_lock(&sdata->u.mgd.mtx); err = __ieee80211_request_smps(sdata, smps_mode); - mutex_unlock(&local->iflist_mtx); + mutex_unlock(&sdata->u.mgd.mtx); return err; } diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index c04a1396cf8..a01d2137fdd 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -92,6 +92,31 @@ static ssize_t sta_inactive_ms_read(struct file *file, char __user *userbuf, } STA_OPS(inactive_ms); + +static ssize_t sta_connected_time_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct sta_info *sta = file->private_data; + struct timespec uptime; + struct tm result; + long connected_time_secs; + char buf[100]; + int res; + do_posix_clock_monotonic_gettime(&uptime); + connected_time_secs = uptime.tv_sec - sta->last_connected; + time_to_tm(connected_time_secs, 0, &result); + result.tm_year -= 70; + result.tm_mday -= 1; + res = scnprintf(buf, sizeof(buf), + "years - %ld\nmonths - %d\ndays - %d\nclock - %d:%d:%d\n\n", + result.tm_year, result.tm_mon, result.tm_mday, + result.tm_hour, result.tm_min, result.tm_sec); + return simple_read_from_buffer(userbuf, count, ppos, buf, res); +} +STA_OPS(connected_time); + + + static ssize_t sta_last_seq_ctrl_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { @@ -324,6 +349,7 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta) DEBUGFS_ADD(flags); DEBUGFS_ADD(num_ps_buf_frames); DEBUGFS_ADD(inactive_ms); + DEBUGFS_ADD(connected_time); DEBUGFS_ADD(last_seq_ctrl); DEBUGFS_ADD(agg_status); DEBUGFS_ADD(dev); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 9c0d62bb0ea..eebf7a67daf 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -41,6 +41,33 @@ static inline void drv_stop(struct ieee80211_local *local) local->started = false; } +#ifdef CONFIG_PM +static inline int drv_suspend(struct ieee80211_local *local, + struct cfg80211_wowlan *wowlan) +{ + int ret; + + might_sleep(); + + trace_drv_suspend(local); + ret = local->ops->suspend(&local->hw, wowlan); + trace_drv_return_int(local, ret); + return ret; +} + +static inline int drv_resume(struct ieee80211_local *local) +{ + int ret; + + might_sleep(); + + trace_drv_resume(local); + ret = local->ops->resume(&local->hw); + trace_drv_return_int(local, ret); + return ret; +} +#endif + static inline int drv_add_interface(struct ieee80211_local *local, struct ieee80211_vif *vif) { @@ -185,12 +212,39 @@ static inline int drv_hw_scan(struct ieee80211_local *local, might_sleep(); - trace_drv_hw_scan(local, sdata, req); + trace_drv_hw_scan(local, sdata); ret = local->ops->hw_scan(&local->hw, &sdata->vif, req); trace_drv_return_int(local, ret); return ret; } +static inline int +drv_sched_scan_start(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct cfg80211_sched_scan_request *req, + struct ieee80211_sched_scan_ies *ies) +{ + int ret; + + might_sleep(); + + trace_drv_sched_scan_start(local, sdata); + ret = local->ops->sched_scan_start(&local->hw, &sdata->vif, + req, ies); + trace_drv_return_int(local, ret); + return ret; +} + +static inline void drv_sched_scan_stop(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) +{ + might_sleep(); + + trace_drv_sched_scan_stop(local, sdata); + local->ops->sched_scan_stop(&local->hw, &sdata->vif); + trace_drv_return_void(local); +} + static inline void drv_sw_scan_start(struct ieee80211_local *local) { might_sleep(); @@ -552,4 +606,35 @@ static inline void drv_get_ringparam(struct ieee80211_local *local, trace_drv_return_void(local); } +static inline bool drv_tx_frames_pending(struct ieee80211_local *local) +{ + bool ret = false; + + might_sleep(); + + trace_drv_tx_frames_pending(local); + if (local->ops->tx_frames_pending) + ret = local->ops->tx_frames_pending(&local->hw); + trace_drv_return_bool(local, ret); + + return ret; +} + +static inline int drv_set_bitrate_mask(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + const struct cfg80211_bitrate_mask *mask) +{ + int ret = -EOPNOTSUPP; + + might_sleep(); + + trace_drv_set_bitrate_mask(local, sdata, mask); + if (local->ops->set_bitrate_mask) + ret = local->ops->set_bitrate_mask(&local->hw, + &sdata->vif, mask); + trace_drv_return_int(local, ret); + + return ret; +} + #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index 45aab80738e..ed9edcbd9aa 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -55,6 +55,70 @@ DECLARE_EVENT_CLASS(local_only_evt, TP_printk(LOCAL_PR_FMT, LOCAL_PR_ARG) ); +DECLARE_EVENT_CLASS(local_sdata_addr_evt, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + __array(char, addr, 6) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + memcpy(__entry->addr, sdata->vif.addr, 6); + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT " addr:%pM", + LOCAL_PR_ARG, VIF_PR_ARG, __entry->addr + ) +); + +DECLARE_EVENT_CLASS(local_u32_evt, + TP_PROTO(struct ieee80211_local *local, u32 value), + TP_ARGS(local, value), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(u32, value) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->value = value; + ), + + TP_printk( + LOCAL_PR_FMT " value:%d", + LOCAL_PR_ARG, __entry->value + ) +); + +DECLARE_EVENT_CLASS(local_sdata_evt, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT, + LOCAL_PR_ARG, VIF_PR_ARG + ) +); + DEFINE_EVENT(local_only_evt, drv_return_void, TP_PROTO(struct ieee80211_local *local), TP_ARGS(local) @@ -74,6 +138,21 @@ TRACE_EVENT(drv_return_int, TP_printk(LOCAL_PR_FMT " - %d", LOCAL_PR_ARG, __entry->ret) ); +TRACE_EVENT(drv_return_bool, + TP_PROTO(struct ieee80211_local *local, bool ret), + TP_ARGS(local, ret), + TP_STRUCT__entry( + LOCAL_ENTRY + __field(bool, ret) + ), + TP_fast_assign( + LOCAL_ASSIGN; + __entry->ret = ret; + ), + TP_printk(LOCAL_PR_FMT " - %s", LOCAL_PR_ARG, (__entry->ret) ? + "true" : "false") +); + TRACE_EVENT(drv_return_u64, TP_PROTO(struct ieee80211_local *local, u64 ret), TP_ARGS(local, ret), @@ -93,33 +172,25 @@ DEFINE_EVENT(local_only_evt, drv_start, TP_ARGS(local) ); +DEFINE_EVENT(local_only_evt, drv_suspend, + TP_PROTO(struct ieee80211_local *local), + TP_ARGS(local) +); + +DEFINE_EVENT(local_only_evt, drv_resume, + TP_PROTO(struct ieee80211_local *local), + TP_ARGS(local) +); + DEFINE_EVENT(local_only_evt, drv_stop, TP_PROTO(struct ieee80211_local *local), TP_ARGS(local) ); -TRACE_EVENT(drv_add_interface, +DEFINE_EVENT(local_sdata_addr_evt, drv_add_interface, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata), - - TP_ARGS(local, sdata), - - TP_STRUCT__entry( - LOCAL_ENTRY - VIF_ENTRY - __array(char, addr, 6) - ), - - TP_fast_assign( - LOCAL_ASSIGN; - VIF_ASSIGN; - memcpy(__entry->addr, sdata->vif.addr, 6); - ), - - TP_printk( - LOCAL_PR_FMT VIF_PR_FMT " addr:%pM", - LOCAL_PR_ARG, VIF_PR_ARG, __entry->addr - ) + TP_ARGS(local, sdata) ); TRACE_EVENT(drv_change_interface, @@ -150,27 +221,10 @@ TRACE_EVENT(drv_change_interface, ) ); -TRACE_EVENT(drv_remove_interface, - TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata), - - TP_ARGS(local, sdata), - - TP_STRUCT__entry( - LOCAL_ENTRY - VIF_ENTRY - __array(char, addr, 6) - ), - - TP_fast_assign( - LOCAL_ASSIGN; - VIF_ASSIGN; - memcpy(__entry->addr, sdata->vif.addr, 6); - ), - - TP_printk( - LOCAL_PR_FMT VIF_PR_FMT " addr:%pM", - LOCAL_PR_ARG, VIF_PR_ARG, __entry->addr - ) +DEFINE_EVENT(local_sdata_addr_evt, drv_remove_interface, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) ); TRACE_EVENT(drv_config, @@ -400,27 +454,22 @@ TRACE_EVENT(drv_update_tkip_key, ) ); -TRACE_EVENT(drv_hw_scan, +DEFINE_EVENT(local_sdata_evt, drv_hw_scan, TP_PROTO(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata, - struct cfg80211_scan_request *req), - - TP_ARGS(local, sdata, req), - - TP_STRUCT__entry( - LOCAL_ENTRY - VIF_ENTRY - ), + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) +); - TP_fast_assign( - LOCAL_ASSIGN; - VIF_ASSIGN; - ), +DEFINE_EVENT(local_sdata_evt, drv_sched_scan_start, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) +); - TP_printk( - LOCAL_PR_FMT VIF_PR_FMT, - LOCAL_PR_ARG,VIF_PR_ARG - ) +DEFINE_EVENT(local_sdata_evt, drv_sched_scan_stop, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) ); DEFINE_EVENT(local_only_evt, drv_sw_scan_start, @@ -489,46 +538,14 @@ TRACE_EVENT(drv_get_tkip_seq, ) ); -TRACE_EVENT(drv_set_frag_threshold, +DEFINE_EVENT(local_u32_evt, drv_set_frag_threshold, TP_PROTO(struct ieee80211_local *local, u32 value), - - TP_ARGS(local, value), - - TP_STRUCT__entry( - LOCAL_ENTRY - __field(u32, value) - ), - - TP_fast_assign( - LOCAL_ASSIGN; - __entry->value = value; - ), - - TP_printk( - LOCAL_PR_FMT " value:%d", - LOCAL_PR_ARG, __entry->value - ) + TP_ARGS(local, value) ); -TRACE_EVENT(drv_set_rts_threshold, +DEFINE_EVENT(local_u32_evt, drv_set_rts_threshold, TP_PROTO(struct ieee80211_local *local, u32 value), - - TP_ARGS(local, value), - - TP_STRUCT__entry( - LOCAL_ENTRY - __field(u32, value) - ), - - TP_fast_assign( - LOCAL_ASSIGN; - __entry->value = value; - ), - - TP_printk( - LOCAL_PR_FMT " value:%d", - LOCAL_PR_ARG, __entry->value - ) + TP_ARGS(local, value) ); TRACE_EVENT(drv_set_coverage_class, @@ -964,11 +981,43 @@ TRACE_EVENT(drv_get_ringparam, ) ); +DEFINE_EVENT(local_only_evt, drv_tx_frames_pending, + TP_PROTO(struct ieee80211_local *local), + TP_ARGS(local) +); + DEFINE_EVENT(local_only_evt, drv_offchannel_tx_cancel_wait, TP_PROTO(struct ieee80211_local *local), TP_ARGS(local) ); +TRACE_EVENT(drv_set_bitrate_mask, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + const struct cfg80211_bitrate_mask *mask), + + TP_ARGS(local, sdata, mask), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + __field(u32, legacy_2g) + __field(u32, legacy_5g) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + __entry->legacy_2g = mask->control[IEEE80211_BAND_2GHZ].legacy; + __entry->legacy_5g = mask->control[IEEE80211_BAND_5GHZ].legacy; + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT " 2G Mask:0x%x 5G Mask:0x%x", + LOCAL_PR_ARG, VIF_PR_ARG, __entry->legacy_2g, __entry->legacy_5g + ) +); + /* * Tracing for API calls that drivers call. */ @@ -1147,6 +1196,42 @@ TRACE_EVENT(api_scan_completed, ) ); +TRACE_EVENT(api_sched_scan_results, + TP_PROTO(struct ieee80211_local *local), + + TP_ARGS(local), + + TP_STRUCT__entry( + LOCAL_ENTRY + ), + + TP_fast_assign( + LOCAL_ASSIGN; + ), + + TP_printk( + LOCAL_PR_FMT, LOCAL_PR_ARG + ) +); + +TRACE_EVENT(api_sched_scan_stopped, + TP_PROTO(struct ieee80211_local *local), + + TP_ARGS(local), + + TP_STRUCT__entry( + LOCAL_ENTRY + ), + + TP_fast_assign( + LOCAL_ASSIGN; + ), + + TP_printk( + LOCAL_PR_FMT, LOCAL_PR_ARG + ) +); + TRACE_EVENT(api_sta_block_awake, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sta *sta, bool block), diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index b9e4b9bd217..7cfc286946c 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -140,14 +140,35 @@ void ieee80211_ba_session_work(struct work_struct *work) sta, tid, WLAN_BACK_RECIPIENT, WLAN_REASON_QSTA_TIMEOUT, true); - tid_tx = sta->ampdu_mlme.tid_tx[tid]; - if (!tid_tx) - continue; + if (test_and_clear_bit(tid, + sta->ampdu_mlme.tid_rx_stop_requested)) + ___ieee80211_stop_rx_ba_session( + sta, tid, WLAN_BACK_RECIPIENT, + WLAN_REASON_UNSPECIFIED, true); + + tid_tx = sta->ampdu_mlme.tid_start_tx[tid]; + if (tid_tx) { + /* + * Assign it over to the normal tid_tx array + * where it "goes live". + */ + spin_lock_bh(&sta->lock); + + sta->ampdu_mlme.tid_start_tx[tid] = NULL; + /* could there be a race? */ + if (sta->ampdu_mlme.tid_tx[tid]) + kfree(tid_tx); + else + ieee80211_assign_tid_tx(sta, tid, tid_tx); + spin_unlock_bh(&sta->lock); - if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) ieee80211_tx_ba_session_handle_start(sta, tid); - else if (test_and_clear_bit(HT_AGG_STATE_WANT_STOP, - &tid_tx->state)) + continue; + } + + tid_tx = rcu_dereference_protected_tid_tx(sta, tid); + if (tid_tx && test_and_clear_bit(HT_AGG_STATE_WANT_STOP, + &tid_tx->state)) ___ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_INITIATOR, true); diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 14883966374..421eaa6b0c2 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -40,7 +40,7 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len) { - u16 auth_alg, auth_transaction, status_code; + u16 auth_alg, auth_transaction; lockdep_assert_held(&sdata->u.ibss.mtx); @@ -49,7 +49,6 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg); auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction); - status_code = le16_to_cpu(mgmt->u.auth.status_code); /* * IEEE 802.11 standard does not require authentication in IBSS @@ -527,8 +526,6 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata) static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; - struct ieee80211_local *local = sdata->local; - struct ieee80211_supported_band *sband; u8 bssid[ETH_ALEN]; u16 capability; int i; @@ -551,8 +548,6 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata) printk(KERN_DEBUG "%s: Creating new IBSS network, BSSID %pM\n", sdata->name, bssid); - sband = local->hw.wiphy->bands[ifibss->channel->band]; - capability = WLAN_CAPABILITY_IBSS; if (ifibss->privacy) @@ -667,12 +662,16 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata, int tx_last_beacon, len = req->len; struct sk_buff *skb; struct ieee80211_mgmt *resp; + struct sk_buff *presp; u8 *pos, *end; lockdep_assert_held(&ifibss->mtx); + presp = rcu_dereference_protected(ifibss->presp, + lockdep_is_held(&ifibss->mtx)); + if (ifibss->state != IEEE80211_IBSS_MLME_JOINED || - len < 24 + 2 || !ifibss->presp) + len < 24 + 2 || !presp) return; tx_last_beacon = drv_tx_last_beacon(local); @@ -710,7 +709,7 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata, } /* Reply with ProbeResp */ - skb = skb_copy(ifibss->presp, GFP_KERNEL); + skb = skb_copy(presp, GFP_KERNEL); if (!skb) return; @@ -990,7 +989,8 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) /* remove beacon */ kfree(sdata->u.ibss.ie); - skb = sdata->u.ibss.presp; + skb = rcu_dereference_protected(sdata->u.ibss.presp, + lockdep_is_held(&sdata->u.ibss.mtx)); rcu_assign_pointer(sdata->u.ibss.presp, NULL); sdata->vif.bss_conf.ibss_joined = false; ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED | diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 89ce1e329b5..090b0ec1e05 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -214,7 +214,7 @@ struct beacon_data { }; struct ieee80211_if_ap { - struct beacon_data *beacon; + struct beacon_data __rcu *beacon; struct list_head vlans; @@ -237,7 +237,7 @@ struct ieee80211_if_vlan { struct list_head list; /* used for all tx if the VLAN is configured to 4-addr mode */ - struct sta_info *sta; + struct sta_info __rcu *sta; }; struct mesh_stats { @@ -442,7 +442,8 @@ struct ieee80211_if_ibss { unsigned long ibss_join_req; /* probe response/beacon for IBSS */ - struct sk_buff *presp, *skb; + struct sk_buff __rcu *presp; + struct sk_buff *skb; enum { IEEE80211_IBSS_MLME_SEARCH, @@ -488,8 +489,13 @@ struct ieee80211_if_mesh { struct mesh_config mshcfg; u32 mesh_seqnum; bool accepting_plinks; - const u8 *vendor_ie; - u8 vendor_ie_len; + const u8 *ie; + u8 ie_len; + enum { + IEEE80211_MESH_SEC_NONE = 0x0, + IEEE80211_MESH_SEC_AUTHED = 0x1, + IEEE80211_MESH_SEC_SECURED = 0x2, + } security; }; #ifdef CONFIG_MAC80211_MESH @@ -562,9 +568,10 @@ struct ieee80211_sub_if_data { struct ieee80211_fragment_entry fragments[IEEE80211_FRAGMENT_MAX]; unsigned int fragment_next; - struct ieee80211_key *keys[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS]; - struct ieee80211_key *default_unicast_key, *default_multicast_key; - struct ieee80211_key *default_mgmt_key; + struct ieee80211_key __rcu *keys[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS]; + struct ieee80211_key __rcu *default_unicast_key; + struct ieee80211_key __rcu *default_multicast_key; + struct ieee80211_key __rcu *default_mgmt_key; u16 sequence_number; __be16 control_port_protocol; @@ -763,6 +770,9 @@ struct ieee80211_local { /* device is started */ bool started; + /* wowlan is enabled -- don't reconfig on resume */ + bool wowlan; + int tx_headroom; /* required headroom for hardware/radiotap */ /* Tasklet and skb queue to process calls from IRQ mode. All frames @@ -794,7 +804,7 @@ struct ieee80211_local { spinlock_t sta_lock; unsigned long num_sta; struct list_head sta_list, sta_pending_list; - struct sta_info *sta_hash[STA_HASH_SIZE]; + struct sta_info __rcu *sta_hash[STA_HASH_SIZE]; struct timer_list sta_cleanup; struct work_struct sta_finish_work; int sta_generation; @@ -836,6 +846,10 @@ struct ieee80211_local { int scan_channel_idx; int scan_ies_len; + bool sched_scanning; + struct ieee80211_sched_scan_ies sched_scan_ies; + struct work_struct sched_scan_stopped_work; + unsigned long leave_oper_channel_time; enum mac80211_scan_state next_scan_state; struct delayed_work scan_work; @@ -1143,6 +1157,12 @@ ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq, void ieee80211_rx_bss_put(struct ieee80211_local *local, struct ieee80211_bss *bss); +/* scheduled scan handling */ +int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, + struct cfg80211_sched_scan_request *req); +int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata); +void ieee80211_sched_scan_stopped_work(struct work_struct *work); + /* off-channel helpers */ bool ieee80211_cfg_on_oper_channel(struct ieee80211_local *local); void ieee80211_offchannel_enable_all_ps(struct ieee80211_local *local, @@ -1246,7 +1266,8 @@ int ieee80211_reconfig(struct ieee80211_local *local); void ieee80211_stop_device(struct ieee80211_local *local); #ifdef CONFIG_PM -int __ieee80211_suspend(struct ieee80211_hw *hw); +int __ieee80211_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan); static inline int __ieee80211_resume(struct ieee80211_hw *hw) { @@ -1259,7 +1280,8 @@ static inline int __ieee80211_resume(struct ieee80211_hw *hw) return ieee80211_reconfig(hw_to_local(hw)); } #else -static inline int __ieee80211_suspend(struct ieee80211_hw *hw) +static inline int __ieee80211_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan) { return 0; } diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 4054399be90..dee30aea9ab 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -384,11 +384,11 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, int i; enum nl80211_channel_type orig_ct; + clear_bit(SDATA_STATE_RUNNING, &sdata->state); + if (local->scan_sdata == sdata) ieee80211_scan_cancel(local); - clear_bit(SDATA_STATE_RUNNING, &sdata->state); - /* * Stop TX on this interface first. */ @@ -449,7 +449,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, /* APs need special treatment */ if (sdata->vif.type == NL80211_IFTYPE_AP) { struct ieee80211_sub_if_data *vlan, *tmpsdata; - struct beacon_data *old_beacon = sdata->u.ap.beacon; + struct beacon_data *old_beacon = + rtnl_dereference(sdata->u.ap.beacon); /* sdata_running will return false, so this will disable */ ieee80211_bss_info_change_notify(sdata, diff --git a/net/mac80211/key.c b/net/mac80211/key.c index af3c56482c8..f825e2f0a57 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -186,7 +186,7 @@ static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, assert_key_lock(sdata->local); if (idx >= 0 && idx < NUM_DEFAULT_KEYS) - key = sdata->keys[idx]; + key = key_mtx_dereference(sdata->local, sdata->keys[idx]); if (uni) rcu_assign_pointer(sdata->default_unicast_key, key); @@ -213,7 +213,7 @@ __ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata, int idx) if (idx >= NUM_DEFAULT_KEYS && idx < NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS) - key = sdata->keys[idx]; + key = key_mtx_dereference(sdata->local, sdata->keys[idx]); rcu_assign_pointer(sdata->default_mgmt_key, key); @@ -257,9 +257,15 @@ static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, else idx = new->conf.keyidx; - defunikey = old && sdata->default_unicast_key == old; - defmultikey = old && sdata->default_multicast_key == old; - defmgmtkey = old && sdata->default_mgmt_key == old; + defunikey = old && + old == key_mtx_dereference(sdata->local, + sdata->default_unicast_key); + defmultikey = old && + old == key_mtx_dereference(sdata->local, + sdata->default_multicast_key); + defmgmtkey = old && + old == key_mtx_dereference(sdata->local, + sdata->default_mgmt_key); if (defunikey && !new) __ieee80211_set_default_key(sdata, -1, true, false); @@ -440,11 +446,11 @@ int ieee80211_key_link(struct ieee80211_key *key, mutex_lock(&sdata->local->key_mtx); if (sta && pairwise) - old_key = sta->ptk; + old_key = key_mtx_dereference(sdata->local, sta->ptk); else if (sta) - old_key = sta->gtk[idx]; + old_key = key_mtx_dereference(sdata->local, sta->gtk[idx]); else - old_key = sdata->keys[idx]; + old_key = key_mtx_dereference(sdata->local, sdata->keys[idx]); __ieee80211_key_replace(sdata, sta, pairwise, old_key, key); __ieee80211_key_destroy(old_key); @@ -458,8 +464,11 @@ int ieee80211_key_link(struct ieee80211_key *key, return ret; } -static void __ieee80211_key_free(struct ieee80211_key *key) +void __ieee80211_key_free(struct ieee80211_key *key) { + if (!key) + return; + /* * Replace key with nothingness if it was ever used. */ @@ -473,9 +482,6 @@ static void __ieee80211_key_free(struct ieee80211_key *key) void ieee80211_key_free(struct ieee80211_local *local, struct ieee80211_key *key) { - if (!key) - return; - mutex_lock(&local->key_mtx); __ieee80211_key_free(key); mutex_unlock(&local->key_mtx); diff --git a/net/mac80211/key.h b/net/mac80211/key.h index 4ddbe27eb57..d801d535133 100644 --- a/net/mac80211/key.h +++ b/net/mac80211/key.h @@ -135,6 +135,7 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, int __must_check ieee80211_key_link(struct ieee80211_key *key, struct ieee80211_sub_if_data *sdata, struct sta_info *sta); +void __ieee80211_key_free(struct ieee80211_key *key); void ieee80211_key_free(struct ieee80211_local *local, struct ieee80211_key *key); void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx, @@ -145,4 +146,7 @@ void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata); void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata); void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata); +#define key_mtx_dereference(local, ref) \ + rcu_dereference_protected(ref, lockdep_is_held(&((local)->key_mtx))) + #endif /* IEEE80211_KEY_H */ diff --git a/net/mac80211/main.c b/net/mac80211/main.c index dc50fc3153e..866f269183c 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -33,12 +33,6 @@ #include "cfg.h" #include "debugfs.h" - -static bool ieee80211_disable_40mhz_24ghz; -module_param(ieee80211_disable_40mhz_24ghz, bool, 0644); -MODULE_PARM_DESC(ieee80211_disable_40mhz_24ghz, - "Disable 40MHz support in the 2.4GHz band"); - static struct lock_class_key ieee80211_rx_skb_queue_class; void ieee80211_configure_filter(struct ieee80211_local *local) @@ -364,7 +358,8 @@ static void ieee80211_restart_work(struct work_struct *work) flush_workqueue(local->workqueue); mutex_lock(&local->mtx); - WARN(test_bit(SCAN_HW_SCANNING, &local->scanning), + WARN(test_bit(SCAN_HW_SCANNING, &local->scanning) || + local->sched_scanning, "%s called with hardware scan in progress\n", __func__); mutex_unlock(&local->mtx); @@ -545,7 +540,9 @@ ieee80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = { }, [NL80211_IFTYPE_MESH_POINT] = { .tx = 0xffff, - .rx = BIT(IEEE80211_STYPE_ACTION >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_AUTH >> 4) | + BIT(IEEE80211_STYPE_DEAUTH >> 4), }, }; @@ -584,8 +581,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, wiphy->flags |= WIPHY_FLAG_NETNS_OK | WIPHY_FLAG_4ADDR_AP | - WIPHY_FLAG_4ADDR_STATION | - WIPHY_FLAG_SUPPORTS_SEPARATE_DEFAULT_KEYS; + WIPHY_FLAG_4ADDR_STATION; if (!ops->set_key) wiphy->flags |= WIPHY_FLAG_IBSS_RSN; @@ -656,6 +652,9 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, setup_timer(&local->dynamic_ps_timer, ieee80211_dynamic_ps_timer, (unsigned long) local); + INIT_WORK(&local->sched_scan_stopped_work, + ieee80211_sched_scan_stopped_work); + sta_info_init(local); for (i = 0; i < IEEE80211_MAX_QUEUES; i++) { @@ -686,7 +685,7 @@ EXPORT_SYMBOL(ieee80211_alloc_hw); int ieee80211_register_hw(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); - int result; + int result, i; enum ieee80211_band band; int channels, max_bitrates; bool supp_ht; @@ -701,6 +700,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) WLAN_CIPHER_SUITE_AES_CMAC }; + if ((hw->wiphy->wowlan.flags || hw->wiphy->wowlan.n_patterns) +#ifdef CONFIG_PM + && (!local->ops->suspend || !local->ops->resume) +#endif + ) + return -EINVAL; + if (hw->max_report_rates == 0) hw->max_report_rates = hw->max_rates; @@ -726,18 +732,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) } channels += sband->n_channels; - /* - * Since ieee80211_disable_40mhz_24ghz is global, we can - * modify the sband's ht data even if the driver uses a - * global structure for that. - */ - if (ieee80211_disable_40mhz_24ghz && - band == IEEE80211_BAND_2GHZ && - sband->ht_cap.ht_supported) { - sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; - sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40; - } - if (max_bitrates < sband->n_bitrates) max_bitrates = sband->n_bitrates; supp_ht = supp_ht || sband->ht_cap.ht_supported; @@ -749,17 +743,44 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) return -ENOMEM; /* if low-level driver supports AP, we also support VLAN */ - if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) - local->hw.wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN); + if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) { + hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN); + hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_AP_VLAN); + } /* mac80211 always supports monitor */ - local->hw.wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); + hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); + hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR); + + /* + * mac80211 doesn't support more than 1 channel, and also not more + * than one IBSS interface + */ + for (i = 0; i < hw->wiphy->n_iface_combinations; i++) { + const struct ieee80211_iface_combination *c; + int j; + + c = &hw->wiphy->iface_combinations[i]; + + if (c->num_different_channels > 1) + return -EINVAL; + + for (j = 0; j < c->n_limits; j++) + if ((c->limits[j].types & BIT(NL80211_IFTYPE_ADHOC)) && + c->limits[j].max > 1) + return -EINVAL; + } #ifndef CONFIG_MAC80211_MESH /* mesh depends on Kconfig, but drivers should set it if they want */ local->hw.wiphy->interface_modes &= ~BIT(NL80211_IFTYPE_MESH_POINT); #endif + /* if the underlying driver supports mesh, mac80211 will (at least) + * provide routing of mesh authentication frames to userspace */ + if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_MESH_POINT)) + local->hw.wiphy->flags |= WIPHY_FLAG_MESH_AUTH; + /* mac80211 supports control port protocol changing */ local->hw.wiphy->flags |= WIPHY_FLAG_CONTROL_PORT_PROTOCOL; @@ -838,6 +859,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if (!local->ops->remain_on_channel) local->hw.wiphy->max_remain_on_channel_duration = 5000; + if (local->ops->sched_scan_start) + local->hw.wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; + result = wiphy_register(local->hw.wiphy); if (result < 0) goto fail_wiphy_register; @@ -861,8 +885,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) * and we need some headroom for passing the frame to monitor * interfaces, but never both at the same time. */ +#ifndef __CHECKER__ BUILD_BUG_ON(IEEE80211_TX_STATUS_HEADROOM != sizeof(struct ieee80211_tx_status_rtap_hdr)); +#endif local->tx_headroom = max_t(unsigned int , local->hw.extra_tx_headroom, sizeof(struct ieee80211_tx_status_rtap_hdr)); @@ -1064,6 +1090,8 @@ static void __exit ieee80211_exit(void) ieee80211s_stop(); ieee80211_iface_exit(); + + rcu_barrier(); } diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 2a57cc02c61..29e9980c8e6 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -279,57 +279,14 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00; *pos++ = 0x00; - if (sdata->u.mesh.vendor_ie) { - int len = sdata->u.mesh.vendor_ie_len; - const u8 *data = sdata->u.mesh.vendor_ie; + if (sdata->u.mesh.ie) { + int len = sdata->u.mesh.ie_len; + const u8 *data = sdata->u.mesh.ie; if (skb_tailroom(skb) > len) memcpy(skb_put(skb, len), data, len); } } -u32 mesh_table_hash(u8 *addr, struct ieee80211_sub_if_data *sdata, struct mesh_table *tbl) -{ - /* Use last four bytes of hw addr and interface index as hash index */ - return jhash_2words(*(u32 *)(addr+2), sdata->dev->ifindex, tbl->hash_rnd) - & tbl->hash_mask; -} - -struct mesh_table *mesh_table_alloc(int size_order) -{ - int i; - struct mesh_table *newtbl; - - newtbl = kmalloc(sizeof(struct mesh_table), GFP_KERNEL); - if (!newtbl) - return NULL; - - newtbl->hash_buckets = kzalloc(sizeof(struct hlist_head) * - (1 << size_order), GFP_KERNEL); - - if (!newtbl->hash_buckets) { - kfree(newtbl); - return NULL; - } - - newtbl->hashwlock = kmalloc(sizeof(spinlock_t) * - (1 << size_order), GFP_KERNEL); - if (!newtbl->hashwlock) { - kfree(newtbl->hash_buckets); - kfree(newtbl); - return NULL; - } - - newtbl->size_order = size_order; - newtbl->hash_mask = (1 << size_order) - 1; - atomic_set(&newtbl->entries, 0); - get_random_bytes(&newtbl->hash_rnd, - sizeof(newtbl->hash_rnd)); - for (i = 0; i <= newtbl->hash_mask; i++) - spin_lock_init(&newtbl->hashwlock[i]); - - return newtbl; -} - static void ieee80211_mesh_path_timer(unsigned long data) { @@ -573,6 +530,10 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, &elems); + /* ignore beacons from secure mesh peers if our security is off */ + if (elems.rsn_len && sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) + return; + if (elems.ds_params && elems.ds_params_len == 1) freq = ieee80211_channel_to_frequency(elems.ds_params[0], band); else @@ -586,9 +547,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, if (elems.mesh_id && elems.mesh_config && mesh_matches_local(&elems, sdata)) { supp_rates = ieee80211_sta_get_rates(local, &elems, band); - - mesh_neighbour_update(mgmt->sa, supp_rates, sdata, - mesh_peer_accepts_plinks(&elems)); + mesh_neighbour_update(mgmt->sa, supp_rates, sdata, &elems); } } @@ -598,7 +557,7 @@ static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata, struct ieee80211_rx_status *rx_status) { switch (mgmt->u.action.category) { - case WLAN_CATEGORY_MESH_PLINK: + case WLAN_CATEGORY_MESH_ACTION: mesh_rx_plink_frame(sdata, mgmt, len, rx_status); break; case WLAN_CATEGORY_MESH_PATH_SEL: @@ -611,12 +570,9 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { struct ieee80211_rx_status *rx_status; - struct ieee80211_if_mesh *ifmsh; struct ieee80211_mgmt *mgmt; u16 stype; - ifmsh = &sdata->u.mesh; - rx_status = IEEE80211_SKB_RXCB(skb); mgmt = (struct ieee80211_mgmt *) skb->data; stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE; diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index b99e230fe31..249e733362e 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -92,7 +92,7 @@ struct mesh_path { u8 dst[ETH_ALEN]; u8 mpp[ETH_ALEN]; /* used for MPP or MAP */ struct ieee80211_sub_if_data *sdata; - struct sta_info *next_hop; + struct sta_info __rcu *next_hop; struct timer_list timer; struct sk_buff_head frame_queue; struct rcu_head rcu; @@ -120,6 +120,7 @@ struct mesh_path { * buckets * @mean_chain_len: maximum average length for the hash buckets' list, if it is * reached, the table will grow + * rcu_head: RCU head to free the table */ struct mesh_table { /* Number of buckets will be 2^N */ @@ -132,6 +133,8 @@ struct mesh_table { int (*copy_node) (struct hlist_node *p, struct mesh_table *newtbl); int size_order; int mean_chain_len; + + struct rcu_head rcu_head; }; /* Recent multicast cache */ @@ -226,7 +229,8 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata); /* Mesh plinks */ void mesh_neighbour_update(u8 *hw_addr, u32 rates, - struct ieee80211_sub_if_data *sdata, bool add); + struct ieee80211_sub_if_data *sdata, + struct ieee802_11_elems *ie); bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie); void mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata); void mesh_plink_broken(struct sta_info *sta); @@ -239,12 +243,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, /* Private interfaces */ /* Mesh tables */ -struct mesh_table *mesh_table_alloc(int size_order); -void mesh_table_free(struct mesh_table *tbl, bool free_leafs); void mesh_mpath_table_grow(void); void mesh_mpp_table_grow(void); -u32 mesh_table_hash(u8 *addr, struct ieee80211_sub_if_data *sdata, - struct mesh_table *tbl); /* Mesh paths */ int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn, __le16 target_rcode, const u8 *ra, struct ieee80211_sub_if_data *sdata); @@ -289,10 +289,6 @@ static inline bool mesh_path_sel_is_hwmp(struct ieee80211_sub_if_data *sdata) return sdata->u.mesh.mesh_pp_id == IEEE80211_PATH_PROTOCOL_HWMP; } -#define for_each_mesh_entry(x, p, node, i) \ - for (i = 0; i <= x->hash_mask; i++) \ - hlist_for_each_entry_rcu(node, p, &x->hash_buckets[i], list) - void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local); void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata); diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 5bf64d7112b..2b18053070c 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -391,7 +391,6 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, (mpath->flags & MESH_PATH_SN_VALID)) { if (SN_GT(mpath->sn, orig_sn) || (mpath->sn == orig_sn && - action == MPATH_PREQ && new_metric >= mpath->metric)) { process = false; fresh_info = false; @@ -561,6 +560,14 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, } +static inline struct sta_info * +next_hop_deref_protected(struct mesh_path *mpath) +{ + return rcu_dereference_protected(mpath->next_hop, + lockdep_is_held(&mpath->state_lock)); +} + + static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, u8 *prep_elem, u32 metric) @@ -600,7 +607,7 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata, spin_unlock_bh(&mpath->state_lock); goto fail; } - memcpy(next_hop, mpath->next_hop->sta.addr, ETH_ALEN); + memcpy(next_hop, next_hop_deref_protected(mpath)->sta.addr, ETH_ALEN); spin_unlock_bh(&mpath->state_lock); --ttl; flags = PREP_IE_FLAGS(prep_elem); @@ -633,7 +640,6 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata, struct mesh_path *mpath; u8 ttl; u8 *ta, *target_addr; - u8 target_flags; u32 target_sn; u16 target_rcode; @@ -644,7 +650,6 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata, return; } ttl--; - target_flags = PERR_IE_TARGET_FLAGS(perr_elem); target_addr = PERR_IE_TARGET_ADDR(perr_elem); target_sn = PERR_IE_TARGET_SN(perr_elem); target_rcode = PERR_IE_TARGET_RCODE(perr_elem); @@ -654,7 +659,8 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata, if (mpath) { spin_lock_bh(&mpath->state_lock); if (mpath->flags & MESH_PATH_ACTIVE && - memcmp(ta, mpath->next_hop->sta.addr, ETH_ALEN) == 0 && + memcmp(ta, next_hop_deref_protected(mpath)->sta.addr, + ETH_ALEN) == 0 && (!(mpath->flags & MESH_PATH_SN_VALID) || SN_GT(target_sn, mpath->sn))) { mpath->flags &= ~MESH_PATH_ACTIVE; @@ -675,12 +681,10 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata, { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct mesh_path *mpath; - u8 *ta; u8 ttl, flags, hopcount; u8 *orig_addr; u32 orig_sn, metric; - ta = mgmt->sa; ttl = rann->rann_ttl; if (ttl <= 1) { ifmsh->mshstats.dropped_frames_ttl++; @@ -918,6 +922,7 @@ int mesh_nexthop_lookup(struct sk_buff *skb, { struct sk_buff *skb_to_free = NULL; struct mesh_path *mpath; + struct sta_info *next_hop; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; u8 *target_addr = hdr->addr3; int err = 0; @@ -945,7 +950,11 @@ int mesh_nexthop_lookup(struct sk_buff *skb, mesh_queue_preq(mpath, PREQ_Q_F_START | PREQ_Q_F_REFRESH); } - memcpy(hdr->addr1, mpath->next_hop->sta.addr, ETH_ALEN); + next_hop = rcu_dereference(mpath->next_hop); + if (next_hop) + memcpy(hdr->addr1, next_hop->sta.addr, ETH_ALEN); + else + err = -ENOENT; } else { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); if (!(mpath->flags & MESH_PATH_RESOLVING)) { @@ -971,20 +980,11 @@ endlookup: void mesh_path_timer(unsigned long data) { - struct ieee80211_sub_if_data *sdata; - struct mesh_path *mpath; - - rcu_read_lock(); - mpath = (struct mesh_path *) data; - mpath = rcu_dereference(mpath); - if (!mpath) - goto endmpathtimer; - sdata = mpath->sdata; + struct mesh_path *mpath = (void *) data; + struct ieee80211_sub_if_data *sdata = mpath->sdata; - if (sdata->local->quiescing) { - rcu_read_unlock(); + if (sdata->local->quiescing) return; - } spin_lock_bh(&mpath->state_lock); if (mpath->flags & MESH_PATH_RESOLVED || @@ -1001,8 +1001,6 @@ void mesh_path_timer(unsigned long data) } spin_unlock_bh(&mpath->state_lock); -endmpathtimer: - rcu_read_unlock(); } void diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index 336ca9d0c5c..0d2faacc3e8 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -36,10 +36,77 @@ struct mpath_node { struct mesh_path *mpath; }; -static struct mesh_table *mesh_paths; -static struct mesh_table *mpp_paths; /* Store paths for MPP&MAP */ +static struct mesh_table __rcu *mesh_paths; +static struct mesh_table __rcu *mpp_paths; /* Store paths for MPP&MAP */ int mesh_paths_generation; + +/* This lock will have the grow table function as writer and add / delete nodes + * as readers. When reading the table (i.e. doing lookups) we are well protected + * by RCU + */ +static DEFINE_RWLOCK(pathtbl_resize_lock); + + +static inline struct mesh_table *resize_dereference_mesh_paths(void) +{ + return rcu_dereference_protected(mesh_paths, + lockdep_is_held(&pathtbl_resize_lock)); +} + +static inline struct mesh_table *resize_dereference_mpp_paths(void) +{ + return rcu_dereference_protected(mpp_paths, + lockdep_is_held(&pathtbl_resize_lock)); +} + +/* + * CAREFUL -- "tbl" must not be an expression, + * in particular not an rcu_dereference(), since + * it's used twice. So it is illegal to do + * for_each_mesh_entry(rcu_dereference(...), ...) + */ +#define for_each_mesh_entry(tbl, p, node, i) \ + for (i = 0; i <= tbl->hash_mask; i++) \ + hlist_for_each_entry_rcu(node, p, &tbl->hash_buckets[i], list) + + +static struct mesh_table *mesh_table_alloc(int size_order) +{ + int i; + struct mesh_table *newtbl; + + newtbl = kmalloc(sizeof(struct mesh_table), GFP_ATOMIC); + if (!newtbl) + return NULL; + + newtbl->hash_buckets = kzalloc(sizeof(struct hlist_head) * + (1 << size_order), GFP_ATOMIC); + + if (!newtbl->hash_buckets) { + kfree(newtbl); + return NULL; + } + + newtbl->hashwlock = kmalloc(sizeof(spinlock_t) * + (1 << size_order), GFP_ATOMIC); + if (!newtbl->hashwlock) { + kfree(newtbl->hash_buckets); + kfree(newtbl); + return NULL; + } + + newtbl->size_order = size_order; + newtbl->hash_mask = (1 << size_order) - 1; + atomic_set(&newtbl->entries, 0); + get_random_bytes(&newtbl->hash_rnd, + sizeof(newtbl->hash_rnd)); + for (i = 0; i <= newtbl->hash_mask; i++) + spin_lock_init(&newtbl->hashwlock[i]); + + return newtbl; +} + static void __mesh_table_free(struct mesh_table *tbl) { kfree(tbl->hash_buckets); @@ -47,7 +114,7 @@ static void __mesh_table_free(struct mesh_table *tbl) kfree(tbl); } -void mesh_table_free(struct mesh_table *tbl, bool free_leafs) +static void mesh_table_free(struct mesh_table *tbl, bool free_leafs) { struct hlist_head *mesh_hash; struct hlist_node *p, *q; @@ -55,60 +122,56 @@ void mesh_table_free(struct mesh_table *tbl, bool free_leafs) mesh_hash = tbl->hash_buckets; for (i = 0; i <= tbl->hash_mask; i++) { - spin_lock(&tbl->hashwlock[i]); + spin_lock_bh(&tbl->hashwlock[i]); hlist_for_each_safe(p, q, &mesh_hash[i]) { tbl->free_node(p, free_leafs); atomic_dec(&tbl->entries); } - spin_unlock(&tbl->hashwlock[i]); + spin_unlock_bh(&tbl->hashwlock[i]); } __mesh_table_free(tbl); } -static struct mesh_table *mesh_table_grow(struct mesh_table *tbl) +static int mesh_table_grow(struct mesh_table *oldtbl, + struct mesh_table *newtbl) { - struct mesh_table *newtbl; struct hlist_head *oldhash; struct hlist_node *p, *q; int i; - if (atomic_read(&tbl->entries) - < tbl->mean_chain_len * (tbl->hash_mask + 1)) - goto endgrow; + if (atomic_read(&oldtbl->entries) + < oldtbl->mean_chain_len * (oldtbl->hash_mask + 1)) + return -EAGAIN; - newtbl = mesh_table_alloc(tbl->size_order + 1); - if (!newtbl) - goto endgrow; - - newtbl->free_node = tbl->free_node; - newtbl->mean_chain_len = tbl->mean_chain_len; - newtbl->copy_node = tbl->copy_node; - atomic_set(&newtbl->entries, atomic_read(&tbl->entries)); + newtbl->free_node = oldtbl->free_node; + newtbl->mean_chain_len = oldtbl->mean_chain_len; + newtbl->copy_node = oldtbl->copy_node; + atomic_set(&newtbl->entries, atomic_read(&oldtbl->entries)); - oldhash = tbl->hash_buckets; - for (i = 0; i <= tbl->hash_mask; i++) + oldhash = oldtbl->hash_buckets; + for (i = 0; i <= oldtbl->hash_mask; i++) hlist_for_each(p, &oldhash[i]) - if (tbl->copy_node(p, newtbl) < 0) + if (oldtbl->copy_node(p, newtbl) < 0) goto errcopy; - return newtbl; + return 0; errcopy: for (i = 0; i <= newtbl->hash_mask; i++) { hlist_for_each_safe(p, q, &newtbl->hash_buckets[i]) - tbl->free_node(p, 0); + oldtbl->free_node(p, 0); } - __mesh_table_free(newtbl); -endgrow: - return NULL; + return -ENOMEM; } +static u32 mesh_table_hash(u8 *addr, struct ieee80211_sub_if_data *sdata, + struct mesh_table *tbl) +{ + /* Use last four bytes of hw addr and interface index as hash index */ + return jhash_2words(*(u32 *)(addr+2), sdata->dev->ifindex, tbl->hash_rnd) + & tbl->hash_mask; +} -/* This lock will have the grow table function as writer and add / delete nodes - * as readers. When reading the table (i.e. doing lookups) we are well protected - * by RCU - */ -static DEFINE_RWLOCK(pathtbl_resize_lock); /** * @@ -218,12 +281,13 @@ struct mesh_path *mpp_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata) */ struct mesh_path *mesh_path_lookup_by_idx(int idx, struct ieee80211_sub_if_data *sdata) { + struct mesh_table *tbl = rcu_dereference(mesh_paths); struct mpath_node *node; struct hlist_node *p; int i; int j = 0; - for_each_mesh_entry(mesh_paths, p, node, i) { + for_each_mesh_entry(tbl, p, node, i) { if (sdata && node->mpath->sdata != sdata) continue; if (j++ == idx) { @@ -253,6 +317,7 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_local *local = sdata->local; + struct mesh_table *tbl; struct mesh_path *mpath, *new_mpath; struct mpath_node *node, *new_node; struct hlist_head *bucket; @@ -280,7 +345,7 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) if (!new_node) goto err_node_alloc; - read_lock(&pathtbl_resize_lock); + read_lock_bh(&pathtbl_resize_lock); memcpy(new_mpath->dst, dst, ETH_ALEN); new_mpath->sdata = sdata; new_mpath->flags = 0; @@ -292,10 +357,12 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) spin_lock_init(&new_mpath->state_lock); init_timer(&new_mpath->timer); - hash_idx = mesh_table_hash(dst, sdata, mesh_paths); - bucket = &mesh_paths->hash_buckets[hash_idx]; + tbl = resize_dereference_mesh_paths(); + + hash_idx = mesh_table_hash(dst, sdata, tbl); + bucket = &tbl->hash_buckets[hash_idx]; - spin_lock(&mesh_paths->hashwlock[hash_idx]); + spin_lock_bh(&tbl->hashwlock[hash_idx]); err = -EEXIST; hlist_for_each_entry(node, n, bucket, list) { @@ -305,14 +372,14 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) } hlist_add_head_rcu(&new_node->list, bucket); - if (atomic_inc_return(&mesh_paths->entries) >= - mesh_paths->mean_chain_len * (mesh_paths->hash_mask + 1)) + if (atomic_inc_return(&tbl->entries) >= + tbl->mean_chain_len * (tbl->hash_mask + 1)) grow = 1; mesh_paths_generation++; - spin_unlock(&mesh_paths->hashwlock[hash_idx]); - read_unlock(&pathtbl_resize_lock); + spin_unlock_bh(&tbl->hashwlock[hash_idx]); + read_unlock_bh(&pathtbl_resize_lock); if (grow) { set_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags); ieee80211_queue_work(&local->hw, &sdata->work); @@ -320,8 +387,8 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) return 0; err_exists: - spin_unlock(&mesh_paths->hashwlock[hash_idx]); - read_unlock(&pathtbl_resize_lock); + spin_unlock_bh(&tbl->hashwlock[hash_idx]); + read_unlock_bh(&pathtbl_resize_lock); kfree(new_node); err_node_alloc: kfree(new_mpath); @@ -330,46 +397,59 @@ err_path_alloc: return err; } +static void mesh_table_free_rcu(struct rcu_head *rcu) +{ + struct mesh_table *tbl = container_of(rcu, struct mesh_table, rcu_head); + + mesh_table_free(tbl, false); +} + void mesh_mpath_table_grow(void) { struct mesh_table *oldtbl, *newtbl; - write_lock(&pathtbl_resize_lock); - oldtbl = mesh_paths; - newtbl = mesh_table_grow(mesh_paths); - if (!newtbl) { - write_unlock(&pathtbl_resize_lock); - return; + write_lock_bh(&pathtbl_resize_lock); + oldtbl = resize_dereference_mesh_paths(); + newtbl = mesh_table_alloc(oldtbl->size_order + 1); + if (!newtbl) + goto out; + if (mesh_table_grow(oldtbl, newtbl) < 0) { + __mesh_table_free(newtbl); + goto out; } rcu_assign_pointer(mesh_paths, newtbl); - write_unlock(&pathtbl_resize_lock); - synchronize_rcu(); - mesh_table_free(oldtbl, false); + call_rcu(&oldtbl->rcu_head, mesh_table_free_rcu); + + out: + write_unlock_bh(&pathtbl_resize_lock); } void mesh_mpp_table_grow(void) { struct mesh_table *oldtbl, *newtbl; - write_lock(&pathtbl_resize_lock); - oldtbl = mpp_paths; - newtbl = mesh_table_grow(mpp_paths); - if (!newtbl) { - write_unlock(&pathtbl_resize_lock); - return; + write_lock_bh(&pathtbl_resize_lock); + oldtbl = resize_dereference_mpp_paths(); + newtbl = mesh_table_alloc(oldtbl->size_order + 1); + if (!newtbl) + goto out; + if (mesh_table_grow(oldtbl, newtbl) < 0) { + __mesh_table_free(newtbl); + goto out; } rcu_assign_pointer(mpp_paths, newtbl); - write_unlock(&pathtbl_resize_lock); + call_rcu(&oldtbl->rcu_head, mesh_table_free_rcu); - synchronize_rcu(); - mesh_table_free(oldtbl, false); + out: + write_unlock_bh(&pathtbl_resize_lock); } int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_local *local = sdata->local; + struct mesh_table *tbl; struct mesh_path *mpath, *new_mpath; struct mpath_node *node, *new_node; struct hlist_head *bucket; @@ -394,7 +474,7 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) if (!new_node) goto err_node_alloc; - read_lock(&pathtbl_resize_lock); + read_lock_bh(&pathtbl_resize_lock); memcpy(new_mpath->dst, dst, ETH_ALEN); memcpy(new_mpath->mpp, mpp, ETH_ALEN); new_mpath->sdata = sdata; @@ -404,10 +484,12 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) new_mpath->exp_time = jiffies; spin_lock_init(&new_mpath->state_lock); - hash_idx = mesh_table_hash(dst, sdata, mpp_paths); - bucket = &mpp_paths->hash_buckets[hash_idx]; + tbl = resize_dereference_mpp_paths(); + + hash_idx = mesh_table_hash(dst, sdata, tbl); + bucket = &tbl->hash_buckets[hash_idx]; - spin_lock(&mpp_paths->hashwlock[hash_idx]); + spin_lock_bh(&tbl->hashwlock[hash_idx]); err = -EEXIST; hlist_for_each_entry(node, n, bucket, list) { @@ -417,12 +499,12 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) } hlist_add_head_rcu(&new_node->list, bucket); - if (atomic_inc_return(&mpp_paths->entries) >= - mpp_paths->mean_chain_len * (mpp_paths->hash_mask + 1)) + if (atomic_inc_return(&tbl->entries) >= + tbl->mean_chain_len * (tbl->hash_mask + 1)) grow = 1; - spin_unlock(&mpp_paths->hashwlock[hash_idx]); - read_unlock(&pathtbl_resize_lock); + spin_unlock_bh(&tbl->hashwlock[hash_idx]); + read_unlock_bh(&pathtbl_resize_lock); if (grow) { set_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags); ieee80211_queue_work(&local->hw, &sdata->work); @@ -430,8 +512,8 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) return 0; err_exists: - spin_unlock(&mpp_paths->hashwlock[hash_idx]); - read_unlock(&pathtbl_resize_lock); + spin_unlock_bh(&tbl->hashwlock[hash_idx]); + read_unlock_bh(&pathtbl_resize_lock); kfree(new_node); err_node_alloc: kfree(new_mpath); @@ -450,6 +532,7 @@ err_path_alloc: */ void mesh_plink_broken(struct sta_info *sta) { + struct mesh_table *tbl; static const u8 bcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; struct mesh_path *mpath; struct mpath_node *node; @@ -458,10 +541,11 @@ void mesh_plink_broken(struct sta_info *sta) int i; rcu_read_lock(); - for_each_mesh_entry(mesh_paths, p, node, i) { + tbl = rcu_dereference(mesh_paths); + for_each_mesh_entry(tbl, p, node, i) { mpath = node->mpath; spin_lock_bh(&mpath->state_lock); - if (mpath->next_hop == sta && + if (rcu_dereference(mpath->next_hop) == sta && mpath->flags & MESH_PATH_ACTIVE && !(mpath->flags & MESH_PATH_FIXED)) { mpath->flags &= ~MESH_PATH_ACTIVE; @@ -490,30 +574,38 @@ void mesh_plink_broken(struct sta_info *sta) */ void mesh_path_flush_by_nexthop(struct sta_info *sta) { + struct mesh_table *tbl; struct mesh_path *mpath; struct mpath_node *node; struct hlist_node *p; int i; - for_each_mesh_entry(mesh_paths, p, node, i) { + rcu_read_lock(); + tbl = rcu_dereference(mesh_paths); + for_each_mesh_entry(tbl, p, node, i) { mpath = node->mpath; - if (mpath->next_hop == sta) + if (rcu_dereference(mpath->next_hop) == sta) mesh_path_del(mpath->dst, mpath->sdata); } + rcu_read_unlock(); } void mesh_path_flush(struct ieee80211_sub_if_data *sdata) { + struct mesh_table *tbl; struct mesh_path *mpath; struct mpath_node *node; struct hlist_node *p; int i; - for_each_mesh_entry(mesh_paths, p, node, i) { + rcu_read_lock(); + tbl = rcu_dereference(mesh_paths); + for_each_mesh_entry(tbl, p, node, i) { mpath = node->mpath; if (mpath->sdata == sdata) mesh_path_del(mpath->dst, mpath->sdata); } + rcu_read_unlock(); } static void mesh_path_node_reclaim(struct rcu_head *rp) @@ -537,6 +629,7 @@ static void mesh_path_node_reclaim(struct rcu_head *rp) */ int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata) { + struct mesh_table *tbl; struct mesh_path *mpath; struct mpath_node *node; struct hlist_head *bucket; @@ -544,20 +637,21 @@ int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata) int hash_idx; int err = 0; - read_lock(&pathtbl_resize_lock); - hash_idx = mesh_table_hash(addr, sdata, mesh_paths); - bucket = &mesh_paths->hash_buckets[hash_idx]; + read_lock_bh(&pathtbl_resize_lock); + tbl = resize_dereference_mesh_paths(); + hash_idx = mesh_table_hash(addr, sdata, tbl); + bucket = &tbl->hash_buckets[hash_idx]; - spin_lock(&mesh_paths->hashwlock[hash_idx]); + spin_lock_bh(&tbl->hashwlock[hash_idx]); hlist_for_each_entry(node, n, bucket, list) { mpath = node->mpath; if (mpath->sdata == sdata && - memcmp(addr, mpath->dst, ETH_ALEN) == 0) { + memcmp(addr, mpath->dst, ETH_ALEN) == 0) { spin_lock_bh(&mpath->state_lock); mpath->flags |= MESH_PATH_RESOLVING; hlist_del_rcu(&node->list); call_rcu(&node->rcu, mesh_path_node_reclaim); - atomic_dec(&mesh_paths->entries); + atomic_dec(&tbl->entries); spin_unlock_bh(&mpath->state_lock); goto enddel; } @@ -566,8 +660,8 @@ int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata) err = -ENXIO; enddel: mesh_paths_generation++; - spin_unlock(&mesh_paths->hashwlock[hash_idx]); - read_unlock(&pathtbl_resize_lock); + spin_unlock_bh(&tbl->hashwlock[hash_idx]); + read_unlock_bh(&pathtbl_resize_lock); return err; } @@ -667,8 +761,10 @@ static void mesh_path_node_free(struct hlist_node *p, bool free_leafs) struct mpath_node *node = hlist_entry(p, struct mpath_node, list); mpath = node->mpath; hlist_del_rcu(p); - if (free_leafs) + if (free_leafs) { + del_timer_sync(&mpath->timer); kfree(mpath); + } kfree(node); } @@ -693,52 +789,60 @@ static int mesh_path_node_copy(struct hlist_node *p, struct mesh_table *newtbl) int mesh_pathtbl_init(void) { - mesh_paths = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); - if (!mesh_paths) + struct mesh_table *tbl_path, *tbl_mpp; + + tbl_path = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); + if (!tbl_path) return -ENOMEM; - mesh_paths->free_node = &mesh_path_node_free; - mesh_paths->copy_node = &mesh_path_node_copy; - mesh_paths->mean_chain_len = MEAN_CHAIN_LEN; + tbl_path->free_node = &mesh_path_node_free; + tbl_path->copy_node = &mesh_path_node_copy; + tbl_path->mean_chain_len = MEAN_CHAIN_LEN; - mpp_paths = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); - if (!mpp_paths) { - mesh_table_free(mesh_paths, true); + tbl_mpp = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); + if (!tbl_mpp) { + mesh_table_free(tbl_path, true); return -ENOMEM; } - mpp_paths->free_node = &mesh_path_node_free; - mpp_paths->copy_node = &mesh_path_node_copy; - mpp_paths->mean_chain_len = MEAN_CHAIN_LEN; + tbl_mpp->free_node = &mesh_path_node_free; + tbl_mpp->copy_node = &mesh_path_node_copy; + tbl_mpp->mean_chain_len = MEAN_CHAIN_LEN; + + /* Need no locking since this is during init */ + RCU_INIT_POINTER(mesh_paths, tbl_path); + RCU_INIT_POINTER(mpp_paths, tbl_mpp); return 0; } void mesh_path_expire(struct ieee80211_sub_if_data *sdata) { + struct mesh_table *tbl; struct mesh_path *mpath; struct mpath_node *node; struct hlist_node *p; int i; - read_lock(&pathtbl_resize_lock); - for_each_mesh_entry(mesh_paths, p, node, i) { + rcu_read_lock(); + tbl = rcu_dereference(mesh_paths); + for_each_mesh_entry(tbl, p, node, i) { if (node->mpath->sdata != sdata) continue; mpath = node->mpath; spin_lock_bh(&mpath->state_lock); if ((!(mpath->flags & MESH_PATH_RESOLVING)) && (!(mpath->flags & MESH_PATH_FIXED)) && - time_after(jiffies, - mpath->exp_time + MESH_PATH_EXPIRE)) { + time_after(jiffies, mpath->exp_time + MESH_PATH_EXPIRE)) { spin_unlock_bh(&mpath->state_lock); mesh_path_del(mpath->dst, mpath->sdata); } else spin_unlock_bh(&mpath->state_lock); } - read_unlock(&pathtbl_resize_lock); + rcu_read_unlock(); } void mesh_pathtbl_unregister(void) { - mesh_table_free(mesh_paths, true); - mesh_table_free(mpp_paths, true); + /* no need for locking during exit path */ + mesh_table_free(rcu_dereference_raw(mesh_paths), true); + mesh_table_free(rcu_dereference_raw(mpp_paths), true); } diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 44b53931ba5..f4adc091788 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -43,7 +43,7 @@ #define dot11MeshMaxPeerLinks(s) (s->u.mesh.mshcfg.dot11MeshMaxPeerLinks) enum plink_frame_type { - PLINK_OPEN = 0, + PLINK_OPEN = 1, PLINK_CONFIRM, PLINK_CLOSE }; @@ -83,7 +83,7 @@ void mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata) */ static inline void mesh_plink_fsm_restart(struct sta_info *sta) { - sta->plink_state = PLINK_LISTEN; + sta->plink_state = NL80211_PLINK_LISTEN; sta->llid = sta->plid = sta->reason = 0; sta->plink_retries = 0; } @@ -105,7 +105,7 @@ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata, if (!sta) return NULL; - sta->flags = WLAN_STA_AUTHORIZED; + sta->flags = WLAN_STA_AUTHORIZED | WLAN_STA_AUTH; sta->sta.supp_rates[local->hw.conf.channel->band] = rates; rate_control_rate_init(sta); @@ -126,11 +126,11 @@ static bool __mesh_plink_deactivate(struct sta_info *sta) struct ieee80211_sub_if_data *sdata = sta->sdata; bool deactivated = false; - if (sta->plink_state == PLINK_ESTAB) { + if (sta->plink_state == NL80211_PLINK_ESTAB) { mesh_plink_dec_estab_count(sdata); deactivated = true; } - sta->plink_state = PLINK_BLOCKED; + sta->plink_state = NL80211_PLINK_BLOCKED; mesh_path_flush_by_nexthop(sta); return deactivated; @@ -161,7 +161,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, __le16 reason) { struct ieee80211_local *local = sdata->local; struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400 + - sdata->u.mesh.vendor_ie_len); + sdata->u.mesh.ie_len); struct ieee80211_mgmt *mgmt; bool include_plid = false; static const u8 meshpeeringproto[] = { 0x00, 0x0F, 0xAC, 0x2A }; @@ -181,8 +181,8 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, IEEE80211_STYPE_ACTION); memcpy(mgmt->da, da, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); - /* BSSID is left zeroed, wildcard value */ - mgmt->u.action.category = WLAN_CATEGORY_MESH_PLINK; + memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); + mgmt->u.action.category = WLAN_CATEGORY_MESH_ACTION; mgmt->u.action.u.plink_action.action_code = action; if (action == PLINK_CLOSE) @@ -237,8 +237,9 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, return 0; } -void mesh_neighbour_update(u8 *hw_addr, u32 rates, struct ieee80211_sub_if_data *sdata, - bool peer_accepting_plinks) +void mesh_neighbour_update(u8 *hw_addr, u32 rates, + struct ieee80211_sub_if_data *sdata, + struct ieee802_11_elems *elems) { struct ieee80211_local *local = sdata->local; struct sta_info *sta; @@ -248,8 +249,14 @@ void mesh_neighbour_update(u8 *hw_addr, u32 rates, struct ieee80211_sub_if_data sta = sta_info_get(sdata, hw_addr); if (!sta) { rcu_read_unlock(); - - sta = mesh_plink_alloc(sdata, hw_addr, rates); + /* Userspace handles peer allocation when security is enabled + * */ + if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) + cfg80211_notify_new_peer_candidate(sdata->dev, hw_addr, + elems->ie_start, elems->total_len, + GFP_KERNEL); + else + sta = mesh_plink_alloc(sdata, hw_addr, rates); if (!sta) return; if (sta_info_insert_rcu(sta)) { @@ -260,7 +267,8 @@ void mesh_neighbour_update(u8 *hw_addr, u32 rates, struct ieee80211_sub_if_data sta->last_rx = jiffies; sta->sta.supp_rates[local->hw.conf.channel->band] = rates; - if (peer_accepting_plinks && sta->plink_state == PLINK_LISTEN && + if (mesh_peer_accepts_plinks(elems) && + sta->plink_state == NL80211_PLINK_LISTEN && sdata->u.mesh.accepting_plinks && sdata->u.mesh.mshcfg.auto_open_plinks) mesh_plink_open(sta); @@ -300,8 +308,8 @@ static void mesh_plink_timer(unsigned long data) sdata = sta->sdata; switch (sta->plink_state) { - case PLINK_OPN_RCVD: - case PLINK_OPN_SNT: + case NL80211_PLINK_OPN_RCVD: + case NL80211_PLINK_OPN_SNT: /* retry timer */ if (sta->plink_retries < dot11MeshMaxRetries(sdata)) { u32 rand; @@ -320,17 +328,17 @@ static void mesh_plink_timer(unsigned long data) } reason = cpu_to_le16(MESH_MAX_RETRIES); /* fall through on else */ - case PLINK_CNF_RCVD: + case NL80211_PLINK_CNF_RCVD: /* confirm timer */ if (!reason) reason = cpu_to_le16(MESH_CONFIRM_TIMEOUT); - sta->plink_state = PLINK_HOLDING; + sta->plink_state = NL80211_PLINK_HOLDING; mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, plid, reason); break; - case PLINK_HOLDING: + case NL80211_PLINK_HOLDING: /* holding timer */ del_timer(&sta->plink_timer); mesh_plink_fsm_restart(sta); @@ -372,14 +380,17 @@ int mesh_plink_open(struct sta_info *sta) __le16 llid; struct ieee80211_sub_if_data *sdata = sta->sdata; + if (!test_sta_flags(sta, WLAN_STA_AUTH)) + return -EPERM; + spin_lock_bh(&sta->lock); get_random_bytes(&llid, 2); sta->llid = llid; - if (sta->plink_state != PLINK_LISTEN) { + if (sta->plink_state != NL80211_PLINK_LISTEN) { spin_unlock_bh(&sta->lock); return -EBUSY; } - sta->plink_state = PLINK_OPN_SNT; + sta->plink_state = NL80211_PLINK_OPN_SNT; mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata)); spin_unlock_bh(&sta->lock); mpl_dbg("Mesh plink: starting establishment with %pM\n", @@ -396,7 +407,7 @@ void mesh_plink_block(struct sta_info *sta) spin_lock_bh(&sta->lock); deactivated = __mesh_plink_deactivate(sta); - sta->plink_state = PLINK_BLOCKED; + sta->plink_state = NL80211_PLINK_BLOCKED; spin_unlock_bh(&sta->lock); if (deactivated) @@ -419,13 +430,13 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m __le16 plid, llid, reason; #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG static const char *mplstates[] = { - [PLINK_LISTEN] = "LISTEN", - [PLINK_OPN_SNT] = "OPN-SNT", - [PLINK_OPN_RCVD] = "OPN-RCVD", - [PLINK_CNF_RCVD] = "CNF_RCVD", - [PLINK_ESTAB] = "ESTAB", - [PLINK_HOLDING] = "HOLDING", - [PLINK_BLOCKED] = "BLOCKED" + [NL80211_PLINK_LISTEN] = "LISTEN", + [NL80211_PLINK_OPN_SNT] = "OPN-SNT", + [NL80211_PLINK_OPN_RCVD] = "OPN-RCVD", + [NL80211_PLINK_CNF_RCVD] = "CNF_RCVD", + [NL80211_PLINK_ESTAB] = "ESTAB", + [NL80211_PLINK_HOLDING] = "HOLDING", + [NL80211_PLINK_BLOCKED] = "BLOCKED" }; #endif @@ -449,6 +460,11 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m mpl_dbg("Mesh plink: missing necessary peer link ie\n"); return; } + if (elems.rsn_len && + sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) { + mpl_dbg("Mesh plink: can't establish link with secure peer\n"); + return; + } ftype = mgmt->u.action.u.plink_action.action_code; ie_len = elems.peer_link_len; @@ -480,7 +496,13 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m return; } - if (sta && sta->plink_state == PLINK_BLOCKED) { + if (sta && !test_sta_flags(sta, WLAN_STA_AUTH)) { + mpl_dbg("Mesh plink: Action frame from non-authed peer\n"); + rcu_read_unlock(); + return; + } + + if (sta && sta->plink_state == NL80211_PLINK_BLOCKED) { rcu_read_unlock(); return; } @@ -550,7 +572,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m event = CNF_ACPT; break; case PLINK_CLOSE: - if (sta->plink_state == PLINK_ESTAB) + if (sta->plink_state == NL80211_PLINK_ESTAB) /* Do not check for llid or plid. This does not * follow the standard but since multiple plinks * per sta are not supported, it is necessary in @@ -585,14 +607,14 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m reason = 0; switch (sta->plink_state) { /* spin_unlock as soon as state is updated at each case */ - case PLINK_LISTEN: + case NL80211_PLINK_LISTEN: switch (event) { case CLS_ACPT: mesh_plink_fsm_restart(sta); spin_unlock_bh(&sta->lock); break; case OPN_ACPT: - sta->plink_state = PLINK_OPN_RCVD; + sta->plink_state = NL80211_PLINK_OPN_RCVD; sta->plid = plid; get_random_bytes(&llid, 2); sta->llid = llid; @@ -609,7 +631,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m } break; - case PLINK_OPN_SNT: + case NL80211_PLINK_OPN_SNT: switch (event) { case OPN_RJCT: case CNF_RJCT: @@ -618,7 +640,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m if (!reason) reason = cpu_to_le16(MESH_CLOSE_RCVD); sta->reason = reason; - sta->plink_state = PLINK_HOLDING; + sta->plink_state = NL80211_PLINK_HOLDING; if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) sta->ignore_plink_timer = true; @@ -630,7 +652,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m break; case OPN_ACPT: /* retry timer is left untouched */ - sta->plink_state = PLINK_OPN_RCVD; + sta->plink_state = NL80211_PLINK_OPN_RCVD; sta->plid = plid; llid = sta->llid; spin_unlock_bh(&sta->lock); @@ -638,7 +660,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m plid, 0); break; case CNF_ACPT: - sta->plink_state = PLINK_CNF_RCVD; + sta->plink_state = NL80211_PLINK_CNF_RCVD; if (!mod_plink_timer(sta, dot11MeshConfirmTimeout(sdata))) sta->ignore_plink_timer = true; @@ -651,7 +673,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m } break; - case PLINK_OPN_RCVD: + case NL80211_PLINK_OPN_RCVD: switch (event) { case OPN_RJCT: case CNF_RJCT: @@ -660,7 +682,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m if (!reason) reason = cpu_to_le16(MESH_CLOSE_RCVD); sta->reason = reason; - sta->plink_state = PLINK_HOLDING; + sta->plink_state = NL80211_PLINK_HOLDING; if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) sta->ignore_plink_timer = true; @@ -678,7 +700,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m break; case CNF_ACPT: del_timer(&sta->plink_timer); - sta->plink_state = PLINK_ESTAB; + sta->plink_state = NL80211_PLINK_ESTAB; spin_unlock_bh(&sta->lock); mesh_plink_inc_estab_count(sdata); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); @@ -691,7 +713,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m } break; - case PLINK_CNF_RCVD: + case NL80211_PLINK_CNF_RCVD: switch (event) { case OPN_RJCT: case CNF_RJCT: @@ -700,7 +722,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m if (!reason) reason = cpu_to_le16(MESH_CLOSE_RCVD); sta->reason = reason; - sta->plink_state = PLINK_HOLDING; + sta->plink_state = NL80211_PLINK_HOLDING; if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) sta->ignore_plink_timer = true; @@ -712,7 +734,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m break; case OPN_ACPT: del_timer(&sta->plink_timer); - sta->plink_state = PLINK_ESTAB; + sta->plink_state = NL80211_PLINK_ESTAB; spin_unlock_bh(&sta->lock); mesh_plink_inc_estab_count(sdata); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); @@ -727,13 +749,13 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m } break; - case PLINK_ESTAB: + case NL80211_PLINK_ESTAB: switch (event) { case CLS_ACPT: reason = cpu_to_le16(MESH_CLOSE_RCVD); sta->reason = reason; deactivated = __mesh_plink_deactivate(sta); - sta->plink_state = PLINK_HOLDING; + sta->plink_state = NL80211_PLINK_HOLDING; llid = sta->llid; mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); spin_unlock_bh(&sta->lock); @@ -753,7 +775,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m break; } break; - case PLINK_HOLDING: + case NL80211_PLINK_HOLDING: switch (event) { case CLS_ACPT: if (del_timer(&sta->plink_timer)) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 865fed4cc18..456cccf26b5 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -232,6 +232,9 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, WARN_ON(!ieee80211_set_channel_type(local, sdata, channel_type)); } + ieee80211_stop_queues_by_reason(&sdata->local->hw, + IEEE80211_QUEUE_STOP_REASON_CSA); + /* channel_type change automatically detected */ ieee80211_hw_config(local, 0); @@ -245,6 +248,9 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, rcu_read_unlock(); } + ieee80211_wake_queues_by_reason(&sdata->local->hw, + IEEE80211_QUEUE_STOP_REASON_CSA); + ht_opmode = le16_to_cpu(hti->operation_mode); /* if bss configuration changed store the new one */ @@ -750,6 +756,8 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work) dynamic_ps_enable_work); struct ieee80211_sub_if_data *sdata = local->ps_sdata; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + unsigned long flags; + int q; /* can only happen when PS was just disabled anyway */ if (!sdata) @@ -758,18 +766,37 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work) if (local->hw.conf.flags & IEEE80211_CONF_PS) return; + /* + * transmission can be stopped by others which leads to + * dynamic_ps_timer expiry. Postpond the ps timer if it + * is not the actual idle state. + */ + spin_lock_irqsave(&local->queue_stop_reason_lock, flags); + for (q = 0; q < local->hw.queues; q++) { + if (local->queue_stop_reasons[q]) { + spin_unlock_irqrestore(&local->queue_stop_reason_lock, + flags); + mod_timer(&local->dynamic_ps_timer, jiffies + + msecs_to_jiffies( + local->hw.conf.dynamic_ps_timeout)); + return; + } + } + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); + if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) && (!(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED))) { netif_tx_stop_all_queues(sdata->dev); - /* - * Flush all the frames queued in the driver before - * going to power save - */ - drv_flush(local, false); - ieee80211_send_nullfunc(local, sdata, 1); - /* Flush once again to get the tx status of nullfunc frame */ - drv_flush(local, false); + if (drv_tx_frames_pending(local)) + mod_timer(&local->dynamic_ps_timer, jiffies + + msecs_to_jiffies( + local->hw.conf.dynamic_ps_timeout)); + else { + ieee80211_send_nullfunc(local, sdata, 1); + /* Flush to get the tx status of nullfunc frame */ + drv_flush(local, false); + } } if (!((local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) && @@ -780,7 +807,7 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work) ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); } - netif_tx_start_all_queues(sdata->dev); + netif_tx_wake_all_queues(sdata->dev); } void ieee80211_dynamic_ps_timer(unsigned long data) @@ -1068,6 +1095,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, local->hw.conf.flags &= ~IEEE80211_CONF_PS; config_changed |= IEEE80211_CONF_CHANGE_PS; } + local->ps_sdata = NULL; ieee80211_hw_config(local, config_changed); diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index e37355193ed..730778a2c90 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -6,7 +6,7 @@ #include "driver-ops.h" #include "led.h" -int __ieee80211_suspend(struct ieee80211_hw *hw) +int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) { struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_sub_if_data *sdata; @@ -14,12 +14,23 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) ieee80211_scan_cancel(local); + if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) { + mutex_lock(&local->sta_mtx); + list_for_each_entry(sta, &local->sta_list, list) { + set_sta_flags(sta, WLAN_STA_BLOCK_BA); + ieee80211_sta_tear_down_BA_sessions(sta, true); + } + mutex_unlock(&local->sta_mtx); + } + ieee80211_stop_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_SUSPEND); /* flush out all packets */ synchronize_net(); + drv_flush(local, false); + local->quiescing = true; /* make quiescing visible to timers everywhere */ mb(); @@ -36,6 +47,16 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) cancel_work_sync(&local->dynamic_ps_enable_work); del_timer_sync(&local->dynamic_ps_timer); + local->wowlan = wowlan && local->open_count; + if (local->wowlan) { + int err = drv_suspend(local, wowlan); + if (err) { + local->quiescing = false; + return err; + } + goto suspend; + } + /* disable keys */ list_for_each_entry(sdata, &local->interfaces, list) ieee80211_disable_keys(sdata); @@ -43,11 +64,6 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) /* tear down aggregation sessions and remove STAs */ mutex_lock(&local->sta_mtx); list_for_each_entry(sta, &local->sta_list, list) { - if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) { - set_sta_flags(sta, WLAN_STA_BLOCK_BA); - ieee80211_sta_tear_down_BA_sessions(sta, true); - } - if (sta->uploaded) { sdata = sta->sdata; if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) @@ -98,6 +114,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) if (local->open_count) ieee80211_stop_device(local); + suspend: local->suspended = true; /* need suspended to be visible before quiescing is false */ barrier(); diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index 778c604d793..58a89554b78 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -417,8 +417,8 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband, tx_time_single = mr->ack_time + mr->perfect_tx_time; /* contention window */ - tx_time_single += t_slot + min(cw, mp->cw_max); - cw = (cw << 1) | 1; + tx_time_single += (t_slot * cw) >> 1; + cw = min((cw << 1) | 1, mp->cw_max); tx_time += tx_time_single; tx_time_cts += tx_time_single + mi->sp_ack_dur; @@ -532,12 +532,21 @@ minstrel_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) mp->hw = hw; mp->update_interval = 100; +#ifdef CONFIG_MAC80211_DEBUGFS + mp->fixed_rate_idx = (u32) -1; + mp->dbg_fixed_rate = debugfs_create_u32("fixed_rate_idx", + S_IRUGO | S_IWUGO, debugfsdir, &mp->fixed_rate_idx); +#endif + return mp; } static void minstrel_free(void *priv) { +#ifdef CONFIG_MAC80211_DEBUGFS + debugfs_remove(((struct minstrel_priv *)priv)->dbg_fixed_rate); +#endif kfree(priv); } diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h index 0f5a83370aa..5d278eccaef 100644 --- a/net/mac80211/rc80211_minstrel.h +++ b/net/mac80211/rc80211_minstrel.h @@ -78,6 +78,18 @@ struct minstrel_priv { unsigned int update_interval; unsigned int lookaround_rate; unsigned int lookaround_rate_mrr; + +#ifdef CONFIG_MAC80211_DEBUGFS + /* + * enable fixed rate processing per RC + * - write static index to debugfs:ieee80211/phyX/rc/fixed_rate_idx + * - write -1 to enable RC processing again + * - setting will be applied on next update + */ + u32 fixed_rate_idx; + struct dentry *dbg_fixed_rate; +#endif + }; struct minstrel_debugfs_info { diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index c06aa3ac6b9..66a1eeb279c 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -464,6 +464,7 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, const struct mcs_group *group; unsigned int tx_time, tx_time_rtscts, tx_time_data; unsigned int cw = mp->cw_min; + unsigned int ctime = 0; unsigned int t_slot = 9; /* FIXME */ unsigned int ampdu_len = MINSTREL_TRUNC(mi->avg_ampdu_len); @@ -480,13 +481,27 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, group = &minstrel_mcs_groups[index / MCS_GROUP_RATES]; tx_time_data = group->duration[index % MCS_GROUP_RATES] * ampdu_len; - tx_time = 2 * (t_slot + mi->overhead + tx_time_data); - tx_time_rtscts = 2 * (t_slot + mi->overhead_rtscts + tx_time_data); + + /* Contention time for first 2 tries */ + ctime = (t_slot * cw) >> 1; + cw = min((cw << 1) | 1, mp->cw_max); + ctime += (t_slot * cw) >> 1; + cw = min((cw << 1) | 1, mp->cw_max); + + /* Total TX time for data and Contention after first 2 tries */ + tx_time = ctime + 2 * (mi->overhead + tx_time_data); + tx_time_rtscts = ctime + 2 * (mi->overhead_rtscts + tx_time_data); + + /* See how many more tries we can fit inside segment size */ do { - cw = (cw << 1) | 1; - cw = min(cw, mp->cw_max); - tx_time += cw + t_slot + mi->overhead; - tx_time_rtscts += cw + t_slot + mi->overhead_rtscts; + /* Contention time for this try */ + ctime = (t_slot * cw) >> 1; + cw = min((cw << 1) | 1, mp->cw_max); + + /* Total TX time after this try */ + tx_time += ctime + mi->overhead + tx_time_data; + tx_time_rtscts += ctime + mi->overhead_rtscts + tx_time_data; + if (tx_time_rtscts < mp->segment_size) mr->retry_count_rtscts++; } while ((tx_time < mp->segment_size) && @@ -594,6 +609,13 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, info->flags |= mi->tx_flags; sample_idx = minstrel_get_sample_rate(mp, mi); + +#ifdef CONFIG_MAC80211_DEBUGFS + /* use fixed index if set */ + if (mp->fixed_rate_idx != -1) + sample_idx = mp->fixed_rate_idx; +#endif + if (sample_idx >= 0) { sample = true; minstrel_ht_set_rate(mp, mi, &ar[0], sample_idx, diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 1f0b010904b..7fa8c6be7bf 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -143,7 +143,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, if (status->flag & RX_FLAG_HT) { /* * MCS information is a separate field in radiotap, - * added below. + * added below. The byte here is needed as padding + * for the channel though, so initialise it to 0. */ *pos = 0; } else { @@ -403,11 +404,13 @@ ieee80211_rx_h_passive_scan(struct ieee80211_rx_data *rx) struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); struct sk_buff *skb = rx->skb; - if (likely(!(status->rx_flags & IEEE80211_RX_IN_SCAN))) + if (likely(!(status->rx_flags & IEEE80211_RX_IN_SCAN) && + !local->sched_scanning)) return RX_CONTINUE; if (test_bit(SCAN_HW_SCANNING, &local->scanning) || - test_bit(SCAN_SW_SCANNING, &local->scanning)) + test_bit(SCAN_SW_SCANNING, &local->scanning) || + local->sched_scanning) return ieee80211_scan_rx(rx->sdata, skb); /* scanning finished during invoking of handlers */ @@ -487,22 +490,26 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) * establisment frame, beacon or probe, drop the frame. */ - if (!rx->sta || sta_plink_state(rx->sta) != PLINK_ESTAB) { + if (!rx->sta || sta_plink_state(rx->sta) != NL80211_PLINK_ESTAB) { struct ieee80211_mgmt *mgmt; if (!ieee80211_is_mgmt(hdr->frame_control)) return RX_DROP_MONITOR; if (ieee80211_is_action(hdr->frame_control)) { + u8 category; mgmt = (struct ieee80211_mgmt *)hdr; - if (mgmt->u.action.category != WLAN_CATEGORY_MESH_PLINK) + category = mgmt->u.action.category; + if (category != WLAN_CATEGORY_MESH_ACTION && + category != WLAN_CATEGORY_SELF_PROTECTED) return RX_DROP_MONITOR; return RX_CONTINUE; } if (ieee80211_is_probe_req(hdr->frame_control) || ieee80211_is_probe_resp(hdr->frame_control) || - ieee80211_is_beacon(hdr->frame_control)) + ieee80211_is_beacon(hdr->frame_control) || + ieee80211_is_auth(hdr->frame_control)) return RX_CONTINUE; return RX_DROP_MONITOR; @@ -650,7 +657,7 @@ static void ieee80211_sta_reorder_release(struct ieee80211_hw *hw, set_release_timer: mod_timer(&tid_agg_rx->reorder_timer, - tid_agg_rx->reorder_time[j] + + tid_agg_rx->reorder_time[j] + 1 + HT_RX_REORDER_BUF_TIMEOUT); } else { del_timer(&tid_agg_rx->reorder_timer); @@ -1585,7 +1592,7 @@ ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx) } static int -__ieee80211_data_to_8023(struct ieee80211_rx_data *rx) +__ieee80211_data_to_8023(struct ieee80211_rx_data *rx, bool *port_control) { struct ieee80211_sub_if_data *sdata = rx->sdata; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; @@ -1593,6 +1600,7 @@ __ieee80211_data_to_8023(struct ieee80211_rx_data *rx) struct ethhdr *ehdr; int ret; + *port_control = false; if (ieee80211_has_a4(hdr->frame_control) && sdata->vif.type == NL80211_IFTYPE_AP_VLAN && !sdata->u.vlan.sta) return -1; @@ -1611,11 +1619,13 @@ __ieee80211_data_to_8023(struct ieee80211_rx_data *rx) return -1; ret = ieee80211_data_to_8023(rx->skb, sdata->vif.addr, sdata->vif.type); - if (ret < 0 || !check_port_control) + if (ret < 0) return ret; ehdr = (struct ethhdr *) rx->skb->data; - if (ehdr->h_proto != rx->sdata->control_port_protocol) + if (ehdr->h_proto == rx->sdata->control_port_protocol) + *port_control = true; + else if (check_port_control) return -1; return 0; @@ -1773,7 +1783,7 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx) ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr, rx->sdata->vif.type, - rx->local->hw.extra_tx_headroom); + rx->local->hw.extra_tx_headroom, true); while (!skb_queue_empty(&frame_list)) { rx->skb = __skb_dequeue(&frame_list); @@ -1916,6 +1926,7 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx) struct net_device *dev = sdata->dev; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; __le16 fc = hdr->frame_control; + bool port_control; int err; if (unlikely(!ieee80211_is_data(hdr->frame_control))) @@ -1932,13 +1943,21 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx) sdata->vif.type == NL80211_IFTYPE_AP) return RX_DROP_MONITOR; - err = __ieee80211_data_to_8023(rx); + err = __ieee80211_data_to_8023(rx, &port_control); if (unlikely(err)) return RX_DROP_UNUSABLE; if (!ieee80211_frame_allowed(rx, fc)) return RX_DROP_MONITOR; + if (rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && + unlikely(port_control) && sdata->bss) { + sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, + u.ap); + dev = sdata->dev; + rx->sdata = sdata; + } + rx->skb->dev = dev; dev->stats.rx_packets++; @@ -2191,7 +2210,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) goto handled; } break; - case WLAN_CATEGORY_MESH_PLINK: + case WLAN_CATEGORY_MESH_ACTION: if (!ieee80211_vif_is_mesh(&sdata->vif)) break; goto queue; @@ -2354,47 +2373,6 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx) return RX_QUEUED; } -static void ieee80211_rx_michael_mic_report(struct ieee80211_hdr *hdr, - struct ieee80211_rx_data *rx) -{ - int keyidx; - unsigned int hdrlen; - - hdrlen = ieee80211_hdrlen(hdr->frame_control); - if (rx->skb->len >= hdrlen + 4) - keyidx = rx->skb->data[hdrlen + 3] >> 6; - else - keyidx = -1; - - if (!rx->sta) { - /* - * Some hardware seem to generate incorrect Michael MIC - * reports; ignore them to avoid triggering countermeasures. - */ - return; - } - - if (!ieee80211_has_protected(hdr->frame_control)) - return; - - if (rx->sdata->vif.type == NL80211_IFTYPE_AP && keyidx) { - /* - * APs with pairwise keys should never receive Michael MIC - * errors for non-zero keyidx because these are reserved for - * group keys and only the AP is sending real multicast - * frames in the BSS. - */ - return; - } - - if (!ieee80211_is_data(hdr->frame_control) && - !ieee80211_is_auth(hdr->frame_control)) - return; - - mac80211_ev_michael_mic_failure(rx->sdata, keyidx, hdr, NULL, - GFP_ATOMIC); -} - /* TODO: use IEEE80211_RX_FRAGMENTED */ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx, struct ieee80211_rate *rate) @@ -2738,12 +2716,6 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx, if (!prepares) return false; - if (status->flag & RX_FLAG_MMIC_ERROR) { - if (status->rx_flags & IEEE80211_RX_RA_MATCH) - ieee80211_rx_michael_mic_report(hdr, rx); - return false; - } - if (!consume) { skb = skb_copy(skb, GFP_ATOMIC); if (!skb) { diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 489b6ad200d..58ffa7d069c 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -170,7 +170,7 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) return RX_CONTINUE; if (skb->len < 24) - return RX_DROP_MONITOR; + return RX_CONTINUE; presp = ieee80211_is_probe_resp(fc); if (presp) { @@ -718,6 +718,11 @@ void ieee80211_scan_work(struct work_struct *work) * without scheduling a new work */ do { + if (!ieee80211_sdata_running(sdata)) { + aborted = true; + goto out_complete; + } + switch (local->next_scan_state) { case SCAN_DECISION: /* if no more bands/channels left, complete scan */ @@ -850,3 +855,122 @@ void ieee80211_scan_cancel(struct ieee80211_local *local) } mutex_unlock(&local->mtx); } + +int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, + struct cfg80211_sched_scan_request *req) +{ + struct ieee80211_local *local = sdata->local; + int ret, i; + + mutex_lock(&sdata->local->mtx); + + if (local->sched_scanning) { + ret = -EBUSY; + goto out; + } + + if (!local->ops->sched_scan_start) { + ret = -ENOTSUPP; + goto out; + } + + for (i = 0; i < IEEE80211_NUM_BANDS; i++) { + local->sched_scan_ies.ie[i] = kzalloc(2 + + IEEE80211_MAX_SSID_LEN + + local->scan_ies_len, + GFP_KERNEL); + if (!local->sched_scan_ies.ie[i]) { + ret = -ENOMEM; + goto out_free; + } + + local->sched_scan_ies.len[i] = + ieee80211_build_preq_ies(local, + local->sched_scan_ies.ie[i], + req->ie, req->ie_len, i, + (u32) -1, 0); + } + + ret = drv_sched_scan_start(local, sdata, req, + &local->sched_scan_ies); + if (ret == 0) { + local->sched_scanning = true; + goto out; + } + +out_free: + while (i > 0) + kfree(local->sched_scan_ies.ie[--i]); +out: + mutex_unlock(&sdata->local->mtx); + return ret; +} + +int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + int ret = 0, i; + + mutex_lock(&sdata->local->mtx); + + if (!local->ops->sched_scan_stop) { + ret = -ENOTSUPP; + goto out; + } + + if (local->sched_scanning) { + for (i = 0; i < IEEE80211_NUM_BANDS; i++) + kfree(local->sched_scan_ies.ie[i]); + + drv_sched_scan_stop(local, sdata); + local->sched_scanning = false; + } +out: + mutex_unlock(&sdata->local->mtx); + + return ret; +} + +void ieee80211_sched_scan_results(struct ieee80211_hw *hw) +{ + struct ieee80211_local *local = hw_to_local(hw); + + trace_api_sched_scan_results(local); + + cfg80211_sched_scan_results(hw->wiphy); +} +EXPORT_SYMBOL(ieee80211_sched_scan_results); + +void ieee80211_sched_scan_stopped_work(struct work_struct *work) +{ + struct ieee80211_local *local = + container_of(work, struct ieee80211_local, + sched_scan_stopped_work); + int i; + + mutex_lock(&local->mtx); + + if (!local->sched_scanning) { + mutex_unlock(&local->mtx); + return; + } + + for (i = 0; i < IEEE80211_NUM_BANDS; i++) + kfree(local->sched_scan_ies.ie[i]); + + local->sched_scanning = false; + + mutex_unlock(&local->mtx); + + cfg80211_sched_scan_stopped(local->hw.wiphy); +} + +void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw) +{ + struct ieee80211_local *local = hw_to_local(hw); + + trace_api_sched_scan_stopped(local); + + ieee80211_queue_work(&local->hw, &local->sched_scan_stopped_work); +} +EXPORT_SYMBOL(ieee80211_sched_scan_stopped); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 52d4b1a695c..b83870bf60f 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -67,7 +67,8 @@ static int sta_info_hash_del(struct ieee80211_local *local, { struct sta_info *s; - s = local->sta_hash[STA_HASH(sta->sta.addr)]; + s = rcu_dereference_protected(local->sta_hash[STA_HASH(sta->sta.addr)], + lockdep_is_held(&local->sta_lock)); if (!s) return -ENOENT; if (s == sta) { @@ -76,9 +77,11 @@ static int sta_info_hash_del(struct ieee80211_local *local, return 0; } - while (s->hnext && s->hnext != sta) - s = s->hnext; - if (s->hnext) { + while (rcu_access_pointer(s->hnext) && + rcu_access_pointer(s->hnext) != sta) + s = rcu_dereference_protected(s->hnext, + lockdep_is_held(&local->sta_lock)); + if (rcu_access_pointer(s->hnext)) { rcu_assign_pointer(s->hnext, sta->hnext); return 0; } @@ -228,6 +231,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, { struct ieee80211_local *local = sdata->local; struct sta_info *sta; + struct timespec uptime; int i; sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp); @@ -245,6 +249,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, sta->sdata = sdata; sta->last_rx = jiffies; + do_posix_clock_monotonic_gettime(&uptime); + sta->last_connected = uptime.tv_sec; ewma_init(&sta->avg_signal, 1024, 8); if (sta_prepare_rate_control(local, sta, gfp)) { @@ -271,7 +277,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ #ifdef CONFIG_MAC80211_MESH - sta->plink_state = PLINK_LISTEN; + sta->plink_state = NL80211_PLINK_LISTEN; init_timer(&sta->plink_timer); #endif @@ -584,7 +590,6 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, { unsigned long flags; struct sk_buff *skb; - struct ieee80211_sub_if_data *sdata; if (skb_queue_empty(&sta->ps_tx_buf)) return false; @@ -601,7 +606,6 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, if (!skb) break; - sdata = sta->sdata; local->total_ps_buffered--; #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG printk(KERN_DEBUG "Buffered frame expired (STA %pM)\n", @@ -609,7 +613,8 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, #endif dev_kfree_skb(skb); - if (skb_queue_empty(&sta->ps_tx_buf)) + if (skb_queue_empty(&sta->ps_tx_buf) && + !test_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF)) sta_info_clear_tim_bit(sta); } @@ -650,10 +655,12 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) if (ret) return ret; + mutex_lock(&local->key_mtx); for (i = 0; i < NUM_DEFAULT_KEYS; i++) - ieee80211_key_free(local, sta->gtk[i]); + __ieee80211_key_free(key_mtx_dereference(local, sta->gtk[i])); if (sta->ptk) - ieee80211_key_free(local, sta->ptk); + __ieee80211_key_free(key_mtx_dereference(local, sta->ptk)); + mutex_unlock(&local->key_mtx); sta->dead = true; @@ -893,6 +900,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) struct ieee80211_local *local = sdata->local; int sent, buffered; + clear_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF); if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS)) drv_sta_notify(local, sdata, STA_NOTIFY_AWAKE, &sta->sta); @@ -985,3 +993,12 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw, ieee80211_queue_work(hw, &sta->drv_unblock_wk); } EXPORT_SYMBOL(ieee80211_sta_block_awake); + +void ieee80211_sta_set_tim(struct ieee80211_sta *pubsta) +{ + struct sta_info *sta = container_of(pubsta, struct sta_info, sta); + + set_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF); + sta_info_set_tim_bit(sta); +} +EXPORT_SYMBOL(ieee80211_sta_set_tim); diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 87b18ba1e0e..a06d64ebc17 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -43,6 +43,8 @@ * be in the queues * @WLAN_STA_PSPOLL: Station sent PS-poll while driver was keeping * station in power-save mode, reply when the driver unblocks. + * @WLAN_STA_PS_DRIVER_BUF: Station has frames pending in driver internal + * buffers. Automatically cleared on station wake-up. */ enum ieee80211_sta_info_flags { WLAN_STA_AUTH = 1<<0, @@ -58,6 +60,7 @@ enum ieee80211_sta_info_flags { WLAN_STA_BLOCK_BA = 1<<11, WLAN_STA_PS_DRIVER = 1<<12, WLAN_STA_PSPOLL = 1<<13, + WLAN_STA_PS_DRIVER_BUF = 1<<14, }; #define STA_TID_NUM 16 @@ -149,51 +152,33 @@ struct tid_ampdu_rx { * * @tid_rx: aggregation info for Rx per TID -- RCU protected * @tid_tx: aggregation info for Tx per TID + * @tid_start_tx: sessions where start was requested * @addba_req_num: number of times addBA request has been sent. * @dialog_token_allocator: dialog token enumerator for each new session; * @work: work struct for starting/stopping aggregation * @tid_rx_timer_expired: bitmap indicating on which TIDs the * RX timer expired until the work for it runs + * @tid_rx_stop_requested: bitmap indicating which BA sessions per TID the + * driver requested to close until the work for it runs * @mtx: mutex to protect all TX data (except non-NULL assignments * to tid_tx[idx], which are protected by the sta spinlock) */ struct sta_ampdu_mlme { struct mutex mtx; /* rx */ - struct tid_ampdu_rx *tid_rx[STA_TID_NUM]; + struct tid_ampdu_rx __rcu *tid_rx[STA_TID_NUM]; unsigned long tid_rx_timer_expired[BITS_TO_LONGS(STA_TID_NUM)]; + unsigned long tid_rx_stop_requested[BITS_TO_LONGS(STA_TID_NUM)]; /* tx */ struct work_struct work; - struct tid_ampdu_tx *tid_tx[STA_TID_NUM]; + struct tid_ampdu_tx __rcu *tid_tx[STA_TID_NUM]; + struct tid_ampdu_tx *tid_start_tx[STA_TID_NUM]; u8 addba_req_num[STA_TID_NUM]; u8 dialog_token_allocator; }; /** - * enum plink_state - state of a mesh peer link finite state machine - * - * @PLINK_LISTEN: initial state, considered the implicit state of non existent - * mesh peer links - * @PLINK_OPN_SNT: mesh plink open frame has been sent to this mesh peer - * @PLINK_OPN_RCVD: mesh plink open frame has been received from this mesh peer - * @PLINK_CNF_RCVD: mesh plink confirm frame has been received from this mesh - * peer - * @PLINK_ESTAB: mesh peer link is established - * @PLINK_HOLDING: mesh peer link is being closed or cancelled - * @PLINK_BLOCKED: all frames transmitted from this mesh plink are discarded - */ -enum plink_state { - PLINK_LISTEN, - PLINK_OPN_SNT, - PLINK_OPN_RCVD, - PLINK_CNF_RCVD, - PLINK_ESTAB, - PLINK_HOLDING, - PLINK_BLOCKED -}; - -/** * struct sta_info - STA information * * This structure collects information about a station that @@ -226,6 +211,7 @@ enum plink_state { * @rx_bytes: Number of bytes received from this STA * @wep_weak_iv_count: number of weak WEP IVs received from this station * @last_rx: time (in jiffies) when last frame was received from this STA + * @last_connected: time (in seconds) when a station got connected * @num_duplicates: number of duplicate frames received from this STA * @rx_fragments: number of received MPDUs * @rx_dropped: number of dropped MPDUs from this STA @@ -260,11 +246,11 @@ enum plink_state { struct sta_info { /* General information, mostly static */ struct list_head list; - struct sta_info *hnext; + struct sta_info __rcu *hnext; struct ieee80211_local *local; struct ieee80211_sub_if_data *sdata; - struct ieee80211_key *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS]; - struct ieee80211_key *ptk; + struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS]; + struct ieee80211_key __rcu *ptk; struct rate_control_ref *rate_ctrl; void *rate_ctrl_priv; spinlock_t lock; @@ -295,6 +281,7 @@ struct sta_info { unsigned long rx_packets, rx_bytes; unsigned long wep_weak_iv_count; unsigned long last_rx; + long last_connected; unsigned long num_duplicates; unsigned long rx_fragments; unsigned long rx_dropped; @@ -334,7 +321,7 @@ struct sta_info { u8 plink_retries; bool ignore_plink_timer; bool plink_timer_was_running; - enum plink_state plink_state; + enum nl80211_plink_state plink_state; u32 plink_timeout; struct timer_list plink_timer; #endif @@ -352,12 +339,12 @@ struct sta_info { struct ieee80211_sta sta; }; -static inline enum plink_state sta_plink_state(struct sta_info *sta) +static inline enum nl80211_plink_state sta_plink_state(struct sta_info *sta) { #ifdef CONFIG_MAC80211_MESH return sta->plink_state; #endif - return PLINK_LISTEN; + return NL80211_PLINK_LISTEN; } static inline void set_sta_flags(struct sta_info *sta, const u32 flags) @@ -416,7 +403,16 @@ static inline u32 get_sta_flags(struct sta_info *sta) return ret; } +void ieee80211_assign_tid_tx(struct sta_info *sta, int tid, + struct tid_ampdu_tx *tid_tx); +static inline struct tid_ampdu_tx * +rcu_dereference_protected_tid_tx(struct sta_info *sta, int tid) +{ + return rcu_dereference_protected(sta->ampdu_mlme.tid_tx[tid], + lockdep_is_held(&sta->lock) || + lockdep_is_held(&sta->ampdu_mlme.mtx)); +} #define STA_HASH_SIZE 256 #define STA_HASH(sta) (sta[5]) diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 3ed3c835fbb..1658efaa2e8 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -446,3 +446,11 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) dev_kfree_skb(skb); } EXPORT_SYMBOL(ieee80211_tx_status); + +void ieee80211_report_low_ack(struct ieee80211_sta *pubsta, u32 num_packets) +{ + struct sta_info *sta = container_of(pubsta, struct sta_info, sta); + cfg80211_cqm_pktloss_notify(sta->sdata->dev, sta->sta.addr, + num_packets, GFP_ATOMIC); +} +EXPORT_SYMBOL(ieee80211_report_low_ack); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index ce4596ed126..3104c844b54 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -237,6 +237,10 @@ ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx) &local->dynamic_ps_disable_work); } + /* Don't restart the timer if we're not disassociated */ + if (!ifmgd->associated) + return TX_CONTINUE; + mod_timer(&local->dynamic_ps_timer, jiffies + msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout)); @@ -1036,14 +1040,11 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx, struct ieee80211_radiotap_iterator iterator; struct ieee80211_radiotap_header *rthdr = (struct ieee80211_radiotap_header *) skb->data; - struct ieee80211_supported_band *sband; bool hw_frag; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len, NULL); - sband = tx->local->hw.wiphy->bands[tx->channel->band]; - info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; tx->flags &= ~IEEE80211_TX_FRAGMENTED; @@ -1150,7 +1151,7 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx, * packet pass through because splicing the frames * back is already done. */ - tid_tx = tx->sta->ampdu_mlme.tid_tx[tid]; + tid_tx = rcu_dereference_protected_tid_tx(tx->sta, tid); if (!tid_tx) { /* do nothing, let packet pass through */ @@ -1442,11 +1443,8 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata, struct ieee80211_tx_data tx; ieee80211_tx_result res_prepare; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - u16 queue; bool result = true; - queue = skb_get_queue_mapping(skb); - if (unlikely(skb->len < 10)) { dev_kfree_skb(skb); return true; @@ -1762,6 +1760,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, ret = NETDEV_TX_OK; goto fail; } + rcu_read_lock(); if (!is_multicast_ether_addr(skb->data)) mppath = mpp_path_lookup(skb->data, sdata); @@ -1776,13 +1775,13 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, !(mppath && compare_ether_addr(mppath->mpp, skb->data))) { hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc, skb->data, skb->data + ETH_ALEN); + rcu_read_unlock(); meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr, sdata, NULL, NULL); } else { int is_mesh_mcast = 1; const u8 *mesh_da; - rcu_read_lock(); if (is_multicast_ether_addr(skb->data)) /* DA TA mSA AE:SA */ mesh_da = skb->data; @@ -2262,7 +2261,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, /* headroom, head length, tail length and maximum TIM length */ skb = dev_alloc_skb(local->tx_headroom + 400 + - sdata->u.mesh.vendor_ie_len); + sdata->u.mesh.ie_len); if (!skb) goto out; @@ -2485,7 +2484,6 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw, { struct ieee80211_local *local = hw_to_local(hw); struct sk_buff *skb = NULL; - struct sta_info *sta; struct ieee80211_tx_data tx; struct ieee80211_sub_if_data *sdata; struct ieee80211_if_ap *bss = NULL; @@ -2527,7 +2525,6 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw, info = IEEE80211_SKB_CB(skb); - sta = tx.sta; tx.flags |= IEEE80211_TX_PS_BUFFERED; tx.channel = local->hw.conf.channel; info->band = tx.channel->band; @@ -2547,8 +2544,9 @@ void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) skb_set_network_header(skb, 0); skb_set_transport_header(skb, 0); - /* send all internal mgmt frames on VO */ - skb_set_queue_mapping(skb, 0); + /* Send all internal mgmt frames on VO. Accordingly set TID to 7. */ + skb_set_queue_mapping(skb, IEEE80211_AC_VO); + skb->priority = 7; /* * The other path calling ieee80211_xmit is from the tasklet, diff --git a/net/mac80211/util.c b/net/mac80211/util.c index ef0560a2346..d3fe2d23748 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1125,9 +1125,27 @@ int ieee80211_reconfig(struct ieee80211_local *local) struct sta_info *sta; int res; +#ifdef CONFIG_PM if (local->suspended) local->resuming = true; + if (local->wowlan) { + local->wowlan = false; + res = drv_resume(local); + if (res < 0) { + local->resuming = false; + return res; + } + if (res == 0) + goto wake_up; + WARN_ON(res > 1); + /* + * res is 1, which means the driver requested + * to go through a regular reset on wakeup. + */ + } +#endif + /* restart hardware */ if (local->open_count) { /* @@ -1258,6 +1276,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) if (ieee80211_sdata_running(sdata)) ieee80211_enable_keys(sdata); + wake_up: ieee80211_wake_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_SUSPEND); diff --git a/net/mac80211/work.c b/net/mac80211/work.c index e73c8cae036..d2e7f0e8667 100644 --- a/net/mac80211/work.c +++ b/net/mac80211/work.c @@ -65,17 +65,9 @@ static void run_again(struct ieee80211_local *local, mod_timer(&local->work_timer, timeout); } -static void work_free_rcu(struct rcu_head *head) -{ - struct ieee80211_work *wk = - container_of(head, struct ieee80211_work, rcu_head); - - kfree(wk); -} - void free_work(struct ieee80211_work *wk) { - call_rcu(&wk->rcu_head, work_free_rcu); + kfree_rcu(wk, rcu_head); } static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len, @@ -198,9 +190,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb; struct ieee80211_mgmt *mgmt; u8 *pos, qos_info; - const u8 *ies; size_t offset = 0, noffset; - int i, len, count, rates_len, supp_rates_len; + int i, count, rates_len, supp_rates_len; u16 capab; struct ieee80211_supported_band *sband; u32 rates = 0; @@ -285,7 +276,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, } /* SSID */ - ies = pos = skb_put(skb, 2 + wk->assoc.ssid_len); + pos = skb_put(skb, 2 + wk->assoc.ssid_len); *pos++ = WLAN_EID_SSID; *pos++ = wk->assoc.ssid_len; memcpy(pos, wk->assoc.ssid, wk->assoc.ssid_len); @@ -295,7 +286,6 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, if (supp_rates_len > 8) supp_rates_len = 8; - len = sband->n_bitrates; pos = skb_put(skb, supp_rates_len + 2); *pos++ = WLAN_EID_SUPP_RATES; *pos++ = supp_rates_len; diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index f1765de2f4b..9dc3b5f26e8 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -87,42 +87,76 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx) struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - /* No way to verify the MIC if the hardware stripped it */ - if (status->flag & RX_FLAG_MMIC_STRIPPED) + /* + * it makes no sense to check for MIC errors on anything other + * than data frames. + */ + if (!ieee80211_is_data_present(hdr->frame_control)) + return RX_CONTINUE; + + /* + * No way to verify the MIC if the hardware stripped it or + * the IV with the key index. In this case we have solely rely + * on the driver to set RX_FLAG_MMIC_ERROR in the event of a + * MIC failure report. + */ + if (status->flag & (RX_FLAG_MMIC_STRIPPED | RX_FLAG_IV_STRIPPED)) { + if (status->flag & RX_FLAG_MMIC_ERROR) + goto mic_fail; + + if (!(status->flag & RX_FLAG_IV_STRIPPED)) + goto update_iv; + return RX_CONTINUE; + } + /* + * Some hardware seems to generate Michael MIC failure reports; even + * though, the frame was not encrypted with TKIP and therefore has no + * MIC. Ignore the flag them to avoid triggering countermeasures. + */ if (!rx->key || rx->key->conf.cipher != WLAN_CIPHER_SUITE_TKIP || - !ieee80211_has_protected(hdr->frame_control) || - !ieee80211_is_data_present(hdr->frame_control)) + !(status->flag & RX_FLAG_DECRYPTED)) return RX_CONTINUE; + if (rx->sdata->vif.type == NL80211_IFTYPE_AP && rx->key->conf.keyidx) { + /* + * APs with pairwise keys should never receive Michael MIC + * errors for non-zero keyidx because these are reserved for + * group keys and only the AP is sending real multicast + * frames in the BSS. ( + */ + return RX_DROP_UNUSABLE; + } + + if (status->flag & RX_FLAG_MMIC_ERROR) + goto mic_fail; + hdrlen = ieee80211_hdrlen(hdr->frame_control); if (skb->len < hdrlen + MICHAEL_MIC_LEN) return RX_DROP_UNUSABLE; data = skb->data + hdrlen; data_len = skb->len - hdrlen - MICHAEL_MIC_LEN; - key = &rx->key->conf.key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY]; michael_mic(key, hdr, data, data_len, mic); - if (memcmp(mic, data + data_len, MICHAEL_MIC_LEN) != 0) { - if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) - return RX_DROP_UNUSABLE; - - mac80211_ev_michael_mic_failure(rx->sdata, rx->key->conf.keyidx, - (void *) skb->data, NULL, - GFP_ATOMIC); - return RX_DROP_UNUSABLE; - } + if (memcmp(mic, data + data_len, MICHAEL_MIC_LEN) != 0) + goto mic_fail; /* remove Michael MIC from payload */ skb_trim(skb, skb->len - MICHAEL_MIC_LEN); +update_iv: /* update IV in key information to be able to detect replays */ rx->key->u.tkip.rx[rx->queue].iv32 = rx->tkip_iv32; rx->key->u.tkip.rx[rx->queue].iv16 = rx->tkip_iv16; return RX_CONTINUE; + +mic_fail: + mac80211_ev_michael_mic_failure(rx->sdata, rx->key->conf.keyidx, + (void *) skb->data, NULL, GFP_ATOMIC); + return RX_DROP_UNUSABLE; } diff --git a/net/netfilter/ipset/ip_set_bitmap_ip.c b/net/netfilter/ipset/ip_set_bitmap_ip.c index a113ff06692..ba2d16607f4 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ip.c +++ b/net/netfilter/ipset/ip_set_bitmap_ip.c @@ -293,7 +293,7 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *tb[], for (; !before(ip_to, ip); ip += map->hosts) { id = ip_to_id(map, ip); - ret = adtfn(set, &id, timeout);; + ret = adtfn(set, &id, timeout); if (ret && !ip_set_eexist(ret, flags)) return ret; diff --git a/net/netfilter/ipset/ip_set_bitmap_ipmac.c b/net/netfilter/ipset/ip_set_bitmap_ipmac.c index 00a33242e90..a274300b6a5 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ipmac.c +++ b/net/netfilter/ipset/ip_set_bitmap_ipmac.c @@ -343,6 +343,10 @@ bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb, ipset_adtfn adtfn = set->variant->adt[adt]; struct ipmac data; + /* MAC can be src only */ + if (!(flags & IPSET_DIM_TWO_SRC)) + return 0; + data.id = ntohl(ip4addr(skb, flags & IPSET_DIM_ONE_SRC)); if (data.id < map->first_ip || data.id > map->last_ip) return -IPSET_ERR_BITMAP_RANGE; diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 9152e69a162..333b0bedf29 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -815,7 +815,7 @@ ip_set_flush(struct sock *ctnl, struct sk_buff *skb, ip_set_id_t i; if (unlikely(protocol_failed(attr))) - return -EPROTO; + return -IPSET_ERR_PROTOCOL; if (!attr[IPSET_ATTR_SETNAME]) { for (i = 0; i < ip_set_max; i++) @@ -1022,8 +1022,9 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb) if (cb->args[1] >= ip_set_max) goto out; - pr_debug("args[0]: %ld args[1]: %ld\n", cb->args[0], cb->args[1]); max = cb->args[0] == DUMP_ONE ? cb->args[1] + 1 : ip_set_max; +dump_last: + pr_debug("args[0]: %ld args[1]: %ld\n", cb->args[0], cb->args[1]); for (; cb->args[1] < max; cb->args[1]++) { index = (ip_set_id_t) cb->args[1]; set = ip_set_list[index]; @@ -1038,8 +1039,8 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb) * so that lists (unions of sets) are dumped last. */ if (cb->args[0] != DUMP_ONE && - !((cb->args[0] == DUMP_ALL) ^ - (set->type->features & IPSET_DUMP_LAST))) + ((cb->args[0] == DUMP_ALL) == + !!(set->type->features & IPSET_DUMP_LAST))) continue; pr_debug("List set: %s\n", set->name); if (!cb->args[2]) { @@ -1083,6 +1084,12 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb) goto release_refcount; } } + /* If we dump all sets, continue with dumping last ones */ + if (cb->args[0] == DUMP_ALL) { + cb->args[0] = DUMP_LAST; + cb->args[1] = 0; + goto dump_last; + } goto out; nla_put_failure: @@ -1093,11 +1100,6 @@ release_refcount: pr_debug("release set %s\n", ip_set_list[index]->name); ip_set_put_byindex(index); } - - /* If we dump all sets, continue with dumping last ones */ - if (cb->args[0] == DUMP_ALL && cb->args[1] >= max && !cb->args[2]) - cb->args[0] = DUMP_LAST; - out: if (nlh) { nlmsg_end(skb, nlh); @@ -1118,7 +1120,7 @@ ip_set_dump(struct sock *ctnl, struct sk_buff *skb, return netlink_dump_start(ctnl, skb, nlh, ip_set_dump_start, - ip_set_dump_done); + ip_set_dump_done, 0); } /* Add, del and test */ diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c index 2dc6de13ac1..059af3120be 100644 --- a/net/netfilter/ipvs/ip_vs_app.c +++ b/net/netfilter/ipvs/ip_vs_app.c @@ -572,11 +572,11 @@ static const struct file_operations ip_vs_app_fops = { .open = ip_vs_app_open, .read = seq_read, .llseek = seq_lseek, - .release = seq_release, + .release = seq_release_net, }; #endif -static int __net_init __ip_vs_app_init(struct net *net) +int __net_init __ip_vs_app_init(struct net *net) { struct netns_ipvs *ipvs = net_ipvs(net); @@ -585,26 +585,17 @@ static int __net_init __ip_vs_app_init(struct net *net) return 0; } -static void __net_exit __ip_vs_app_cleanup(struct net *net) +void __net_exit __ip_vs_app_cleanup(struct net *net) { proc_net_remove(net, "ip_vs_app"); } -static struct pernet_operations ip_vs_app_ops = { - .init = __ip_vs_app_init, - .exit = __ip_vs_app_cleanup, -}; - int __init ip_vs_app_init(void) { - int rv; - - rv = register_pernet_subsys(&ip_vs_app_ops); - return rv; + return 0; } void ip_vs_app_cleanup(void) { - unregister_pernet_subsys(&ip_vs_app_ops); } diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index c97bd45975b..bf28ac2fc99 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -1046,7 +1046,7 @@ static const struct file_operations ip_vs_conn_fops = { .open = ip_vs_conn_open, .read = seq_read, .llseek = seq_lseek, - .release = seq_release, + .release = seq_release_net, }; static const char *ip_vs_origin_name(unsigned flags) @@ -1114,7 +1114,7 @@ static const struct file_operations ip_vs_conn_sync_fops = { .open = ip_vs_conn_sync_open, .read = seq_read, .llseek = seq_lseek, - .release = seq_release, + .release = seq_release_net, }; #endif @@ -1258,22 +1258,17 @@ int __net_init __ip_vs_conn_init(struct net *net) return 0; } -static void __net_exit __ip_vs_conn_cleanup(struct net *net) +void __net_exit __ip_vs_conn_cleanup(struct net *net) { /* flush all the connection entries first */ ip_vs_conn_flush(net); proc_net_remove(net, "ip_vs_conn"); proc_net_remove(net, "ip_vs_conn_sync"); } -static struct pernet_operations ipvs_conn_ops = { - .init = __ip_vs_conn_init, - .exit = __ip_vs_conn_cleanup, -}; int __init ip_vs_conn_init(void) { int idx; - int retc; /* Compute size and mask */ ip_vs_conn_tab_size = 1 << ip_vs_conn_tab_bits; @@ -1309,17 +1304,14 @@ int __init ip_vs_conn_init(void) rwlock_init(&__ip_vs_conntbl_lock_array[idx].l); } - retc = register_pernet_subsys(&ipvs_conn_ops); - /* calculate the random value for connection hash */ get_random_bytes(&ip_vs_conn_rnd, sizeof(ip_vs_conn_rnd)); - return retc; + return 0; } void ip_vs_conn_cleanup(void) { - unregister_pernet_subsys(&ipvs_conn_ops); /* Release the empty cache */ kmem_cache_destroy(ip_vs_conn_cachep); vfree(ip_vs_conn_tab); diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 07accf6b240..bfa808f4da1 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1113,6 +1113,9 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af) return NF_ACCEPT; net = skb_net(skb); + if (!net_ipvs(net)->enable) + return NF_ACCEPT; + ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); #ifdef CONFIG_IP_VS_IPV6 if (af == AF_INET6) { @@ -1343,6 +1346,7 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum) return NF_ACCEPT; /* The packet looks wrong, ignore */ net = skb_net(skb); + pd = ip_vs_proto_data_get(net, cih->protocol); if (!pd) return NF_ACCEPT; @@ -1378,15 +1382,7 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum) ip_vs_in_stats(cp, skb); if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol) offset += 2 * sizeof(__u16); - verdict = ip_vs_icmp_xmit(skb, cp, pp, offset); - /* LOCALNODE from FORWARD hook is not supported */ - if (verdict == NF_ACCEPT && hooknum == NF_INET_FORWARD && - skb_rtable(skb)->rt_flags & RTCF_LOCAL) { - IP_VS_DBG(1, "%s(): " - "local delivery to %pI4 but in FORWARD\n", - __func__, &skb_rtable(skb)->rt_dst); - verdict = NF_DROP; - } + verdict = ip_vs_icmp_xmit(skb, cp, pp, offset, hooknum); out: __ip_vs_conn_put(cp); @@ -1408,7 +1404,6 @@ ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum) struct ip_vs_protocol *pp; struct ip_vs_proto_data *pd; unsigned int offset, verdict; - struct rt6_info *rt; *related = 1; @@ -1470,23 +1465,12 @@ ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum) if (!cp) return NF_ACCEPT; - verdict = NF_DROP; - /* do the statistics and put it back */ ip_vs_in_stats(cp, skb); if (IPPROTO_TCP == cih->nexthdr || IPPROTO_UDP == cih->nexthdr || IPPROTO_SCTP == cih->nexthdr) offset += 2 * sizeof(__u16); - verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, offset); - /* LOCALNODE from FORWARD hook is not supported */ - if (verdict == NF_ACCEPT && hooknum == NF_INET_FORWARD && - (rt = (struct rt6_info *) skb_dst(skb)) && - rt->rt6i_dev && rt->rt6i_dev->flags & IFF_LOOPBACK) { - IP_VS_DBG(1, "%s(): " - "local delivery to %pI6 but in FORWARD\n", - __func__, &rt->rt6i_dst); - verdict = NF_DROP; - } + verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, offset, hooknum); __ip_vs_conn_put(cp); @@ -1529,6 +1513,11 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) IP_VS_DBG_ADDR(af, &iph.daddr), hooknum); return NF_ACCEPT; } + /* ipvs enabled in this netns ? */ + net = skb_net(skb); + if (!net_ipvs(net)->enable) + return NF_ACCEPT; + ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); /* Bad... Do not break raw sockets */ @@ -1562,7 +1551,6 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); } - net = skb_net(skb); /* Protocol supported? */ pd = ip_vs_proto_data_get(net, iph.protocol); if (unlikely(!pd)) @@ -1588,7 +1576,6 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) } IP_VS_DBG_PKT(11, af, pp, skb, 0, "Incoming packet"); - net = skb_net(skb); ipvs = net_ipvs(net); /* Check the server status */ if (cp->dest && !(cp->dest->flags & IP_VS_DEST_F_AVAILABLE)) { @@ -1743,10 +1730,16 @@ ip_vs_forward_icmp(unsigned int hooknum, struct sk_buff *skb, int (*okfn)(struct sk_buff *)) { int r; + struct net *net; if (ip_hdr(skb)->protocol != IPPROTO_ICMP) return NF_ACCEPT; + /* ipvs enabled in this netns ? */ + net = skb_net(skb); + if (!net_ipvs(net)->enable) + return NF_ACCEPT; + return ip_vs_in_icmp(skb, &r, hooknum); } @@ -1757,10 +1750,16 @@ ip_vs_forward_icmp_v6(unsigned int hooknum, struct sk_buff *skb, int (*okfn)(struct sk_buff *)) { int r; + struct net *net; if (ipv6_hdr(skb)->nexthdr != IPPROTO_ICMPV6) return NF_ACCEPT; + /* ipvs enabled in this netns ? */ + net = skb_net(skb); + if (!net_ipvs(net)->enable) + return NF_ACCEPT; + return ip_vs_in_icmp_v6(skb, &r, hooknum); } #endif @@ -1884,19 +1883,70 @@ static int __net_init __ip_vs_init(struct net *net) pr_err("%s(): no memory.\n", __func__); return -ENOMEM; } + /* Hold the beast until a service is registerd */ + ipvs->enable = 0; ipvs->net = net; /* Counters used for creating unique names */ ipvs->gen = atomic_read(&ipvs_netns_cnt); atomic_inc(&ipvs_netns_cnt); net->ipvs = ipvs; + + if (__ip_vs_estimator_init(net) < 0) + goto estimator_fail; + + if (__ip_vs_control_init(net) < 0) + goto control_fail; + + if (__ip_vs_protocol_init(net) < 0) + goto protocol_fail; + + if (__ip_vs_app_init(net) < 0) + goto app_fail; + + if (__ip_vs_conn_init(net) < 0) + goto conn_fail; + + if (__ip_vs_sync_init(net) < 0) + goto sync_fail; + printk(KERN_INFO "IPVS: Creating netns size=%zu id=%d\n", sizeof(struct netns_ipvs), ipvs->gen); return 0; +/* + * Error handling + */ + +sync_fail: + __ip_vs_conn_cleanup(net); +conn_fail: + __ip_vs_app_cleanup(net); +app_fail: + __ip_vs_protocol_cleanup(net); +protocol_fail: + __ip_vs_control_cleanup(net); +control_fail: + __ip_vs_estimator_cleanup(net); +estimator_fail: + return -ENOMEM; } static void __net_exit __ip_vs_cleanup(struct net *net) { - IP_VS_DBG(10, "ipvs netns %d released\n", net_ipvs(net)->gen); + __ip_vs_service_cleanup(net); /* ip_vs_flush() with locks */ + __ip_vs_conn_cleanup(net); + __ip_vs_app_cleanup(net); + __ip_vs_protocol_cleanup(net); + __ip_vs_control_cleanup(net); + __ip_vs_estimator_cleanup(net); + IP_VS_DBG(2, "ipvs netns %d released\n", net_ipvs(net)->gen); +} + +static void __net_exit __ip_vs_dev_cleanup(struct net *net) +{ + EnterFunction(2); + net_ipvs(net)->enable = 0; /* Disable packet reception */ + __ip_vs_sync_cleanup(net); + LeaveFunction(2); } static struct pernet_operations ipvs_core_ops = { @@ -1906,6 +1956,10 @@ static struct pernet_operations ipvs_core_ops = { .size = sizeof(struct netns_ipvs), }; +static struct pernet_operations ipvs_core_dev_ops = { + .exit = __ip_vs_dev_cleanup, +}; + /* * Initialize IP Virtual Server */ @@ -1913,10 +1967,6 @@ static int __init ip_vs_init(void) { int ret; - ret = register_pernet_subsys(&ipvs_core_ops); /* Alloc ip_vs struct */ - if (ret < 0) - return ret; - ip_vs_estimator_init(); ret = ip_vs_control_init(); if (ret < 0) { @@ -1944,15 +1994,28 @@ static int __init ip_vs_init(void) goto cleanup_conn; } + ret = register_pernet_subsys(&ipvs_core_ops); /* Alloc ip_vs struct */ + if (ret < 0) + goto cleanup_sync; + + ret = register_pernet_device(&ipvs_core_dev_ops); + if (ret < 0) + goto cleanup_sub; + ret = nf_register_hooks(ip_vs_ops, ARRAY_SIZE(ip_vs_ops)); if (ret < 0) { pr_err("can't register hooks.\n"); - goto cleanup_sync; + goto cleanup_dev; } pr_info("ipvs loaded.\n"); + return ret; +cleanup_dev: + unregister_pernet_device(&ipvs_core_dev_ops); +cleanup_sub: + unregister_pernet_subsys(&ipvs_core_ops); cleanup_sync: ip_vs_sync_cleanup(); cleanup_conn: @@ -1964,20 +2027,20 @@ cleanup_sync: ip_vs_control_cleanup(); cleanup_estimator: ip_vs_estimator_cleanup(); - unregister_pernet_subsys(&ipvs_core_ops); /* free ip_vs struct */ return ret; } static void __exit ip_vs_cleanup(void) { nf_unregister_hooks(ip_vs_ops, ARRAY_SIZE(ip_vs_ops)); + unregister_pernet_device(&ipvs_core_dev_ops); + unregister_pernet_subsys(&ipvs_core_ops); /* free ip_vs struct */ ip_vs_sync_cleanup(); ip_vs_conn_cleanup(); ip_vs_app_cleanup(); ip_vs_protocol_cleanup(); ip_vs_control_cleanup(); ip_vs_estimator_cleanup(); - unregister_pernet_subsys(&ipvs_core_ops); /* free ip_vs struct */ pr_info("ipvs unloaded.\n"); } diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 9930f340908..699c79a5565 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -69,6 +69,11 @@ int ip_vs_get_debug_level(void) } #endif + +/* Protos */ +static void __ip_vs_del_service(struct ip_vs_service *svc); + + #ifdef CONFIG_IP_VS_IPV6 /* Taken from rt6_fill_node() in net/ipv6/route.c, is there a better way? */ static int __ip_vs_addr_is_local_v6(struct net *net, @@ -1214,6 +1219,8 @@ ip_vs_add_service(struct net *net, struct ip_vs_service_user_kern *u, write_unlock_bh(&__ip_vs_svc_lock); *svc_p = svc; + /* Now there is a service - full throttle */ + ipvs->enable = 1; return 0; @@ -1472,6 +1479,84 @@ static int ip_vs_flush(struct net *net) return 0; } +/* + * Delete service by {netns} in the service table. + * Called by __ip_vs_cleanup() + */ +void __ip_vs_service_cleanup(struct net *net) +{ + EnterFunction(2); + /* Check for "full" addressed entries */ + mutex_lock(&__ip_vs_mutex); + ip_vs_flush(net); + mutex_unlock(&__ip_vs_mutex); + LeaveFunction(2); +} +/* + * Release dst hold by dst_cache + */ +static inline void +__ip_vs_dev_reset(struct ip_vs_dest *dest, struct net_device *dev) +{ + spin_lock_bh(&dest->dst_lock); + if (dest->dst_cache && dest->dst_cache->dev == dev) { + IP_VS_DBG_BUF(3, "Reset dev:%s dest %s:%u ,dest->refcnt=%d\n", + dev->name, + IP_VS_DBG_ADDR(dest->af, &dest->addr), + ntohs(dest->port), + atomic_read(&dest->refcnt)); + ip_vs_dst_reset(dest); + } + spin_unlock_bh(&dest->dst_lock); + +} +/* + * Netdev event receiver + * Currently only NETDEV_UNREGISTER is handled, i.e. if we hold a reference to + * a device that is "unregister" it must be released. + */ +static int ip_vs_dst_event(struct notifier_block *this, unsigned long event, + void *ptr) +{ + struct net_device *dev = ptr; + struct net *net = dev_net(dev); + struct ip_vs_service *svc; + struct ip_vs_dest *dest; + unsigned int idx; + + if (event != NETDEV_UNREGISTER) + return NOTIFY_DONE; + IP_VS_DBG(3, "%s() dev=%s\n", __func__, dev->name); + EnterFunction(2); + mutex_lock(&__ip_vs_mutex); + for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { + list_for_each_entry(svc, &ip_vs_svc_table[idx], s_list) { + if (net_eq(svc->net, net)) { + list_for_each_entry(dest, &svc->destinations, + n_list) { + __ip_vs_dev_reset(dest, dev); + } + } + } + + list_for_each_entry(svc, &ip_vs_svc_fwm_table[idx], f_list) { + if (net_eq(svc->net, net)) { + list_for_each_entry(dest, &svc->destinations, + n_list) { + __ip_vs_dev_reset(dest, dev); + } + } + + } + } + + list_for_each_entry(dest, &net_ipvs(net)->dest_trash, n_list) { + __ip_vs_dev_reset(dest, dev); + } + mutex_unlock(&__ip_vs_mutex); + LeaveFunction(2); + return NOTIFY_DONE; +} /* * Zero counters in a service or all services @@ -1981,7 +2066,7 @@ static const struct file_operations ip_vs_info_fops = { .open = ip_vs_info_open, .read = seq_read, .llseek = seq_lseek, - .release = seq_release_private, + .release = seq_release_net, }; static int ip_vs_stats_show(struct seq_file *seq, void *v) @@ -2021,7 +2106,7 @@ static const struct file_operations ip_vs_stats_fops = { .open = ip_vs_stats_seq_open, .read = seq_read, .llseek = seq_lseek, - .release = single_release, + .release = single_release_net, }; static int ip_vs_stats_percpu_show(struct seq_file *seq, void *v) @@ -2090,7 +2175,7 @@ static const struct file_operations ip_vs_stats_percpu_fops = { .open = ip_vs_stats_percpu_seq_open, .read = seq_read, .llseek = seq_lseek, - .release = single_release, + .release = single_release_net, }; #endif @@ -3585,6 +3670,10 @@ void __net_init __ip_vs_control_cleanup_sysctl(struct net *net) { } #endif +static struct notifier_block ip_vs_dst_notifier = { + .notifier_call = ip_vs_dst_event, +}; + int __net_init __ip_vs_control_init(struct net *net) { int idx; @@ -3623,7 +3712,7 @@ err: return -ENOMEM; } -static void __net_exit __ip_vs_control_cleanup(struct net *net) +void __net_exit __ip_vs_control_cleanup(struct net *net) { struct netns_ipvs *ipvs = net_ipvs(net); @@ -3636,11 +3725,6 @@ static void __net_exit __ip_vs_control_cleanup(struct net *net) free_percpu(ipvs->tot_stats.cpustats); } -static struct pernet_operations ipvs_control_ops = { - .init = __ip_vs_control_init, - .exit = __ip_vs_control_cleanup, -}; - int __init ip_vs_control_init(void) { int idx; @@ -3654,33 +3738,32 @@ int __init ip_vs_control_init(void) INIT_LIST_HEAD(&ip_vs_svc_fwm_table[idx]); } - ret = register_pernet_subsys(&ipvs_control_ops); - if (ret) { - pr_err("cannot register namespace.\n"); - goto err; - } - smp_wmb(); /* Do we really need it now ? */ ret = nf_register_sockopt(&ip_vs_sockopts); if (ret) { pr_err("cannot register sockopt.\n"); - goto err_net; + goto err_sock; } ret = ip_vs_genl_register(); if (ret) { pr_err("cannot register Generic Netlink interface.\n"); - nf_unregister_sockopt(&ip_vs_sockopts); - goto err_net; + goto err_genl; } + ret = register_netdevice_notifier(&ip_vs_dst_notifier); + if (ret < 0) + goto err_notf; + LeaveFunction(2); return 0; -err_net: - unregister_pernet_subsys(&ipvs_control_ops); -err: +err_notf: + ip_vs_genl_unregister(); +err_genl: + nf_unregister_sockopt(&ip_vs_sockopts); +err_sock: return ret; } @@ -3688,7 +3771,6 @@ err: void ip_vs_control_cleanup(void) { EnterFunction(2); - unregister_pernet_subsys(&ipvs_control_ops); ip_vs_genl_unregister(); nf_unregister_sockopt(&ip_vs_sockopts); LeaveFunction(2); diff --git a/net/netfilter/ipvs/ip_vs_est.c b/net/netfilter/ipvs/ip_vs_est.c index 8c8766ca56a..508cce98777 100644 --- a/net/netfilter/ipvs/ip_vs_est.c +++ b/net/netfilter/ipvs/ip_vs_est.c @@ -192,7 +192,7 @@ void ip_vs_read_estimator(struct ip_vs_stats_user *dst, dst->outbps = (e->outbps + 0xF) >> 5; } -static int __net_init __ip_vs_estimator_init(struct net *net) +int __net_init __ip_vs_estimator_init(struct net *net) { struct netns_ipvs *ipvs = net_ipvs(net); @@ -203,24 +203,16 @@ static int __net_init __ip_vs_estimator_init(struct net *net) return 0; } -static void __net_exit __ip_vs_estimator_exit(struct net *net) +void __net_exit __ip_vs_estimator_cleanup(struct net *net) { del_timer_sync(&net_ipvs(net)->est_timer); } -static struct pernet_operations ip_vs_app_ops = { - .init = __ip_vs_estimator_init, - .exit = __ip_vs_estimator_exit, -}; int __init ip_vs_estimator_init(void) { - int rv; - - rv = register_pernet_subsys(&ip_vs_app_ops); - return rv; + return 0; } void ip_vs_estimator_cleanup(void) { - unregister_pernet_subsys(&ip_vs_app_ops); } diff --git a/net/netfilter/ipvs/ip_vs_ftp.c b/net/netfilter/ipvs/ip_vs_ftp.c index 6b5dd6ddaae..af63553fa33 100644 --- a/net/netfilter/ipvs/ip_vs_ftp.c +++ b/net/netfilter/ipvs/ip_vs_ftp.c @@ -411,25 +411,35 @@ static struct ip_vs_app ip_vs_ftp = { static int __net_init __ip_vs_ftp_init(struct net *net) { int i, ret; - struct ip_vs_app *app = &ip_vs_ftp; + struct ip_vs_app *app; + struct netns_ipvs *ipvs = net_ipvs(net); + + app = kmemdup(&ip_vs_ftp, sizeof(struct ip_vs_app), GFP_KERNEL); + if (!app) + return -ENOMEM; + INIT_LIST_HEAD(&app->a_list); + INIT_LIST_HEAD(&app->incs_list); + ipvs->ftp_app = app; ret = register_ip_vs_app(net, app); if (ret) - return ret; + goto err_exit; for (i=0; i<IP_VS_APP_MAX_PORTS; i++) { if (!ports[i]) continue; ret = register_ip_vs_app_inc(net, app, app->protocol, ports[i]); if (ret) - break; + goto err_unreg; pr_info("%s: loaded support on port[%d] = %d\n", app->name, i, ports[i]); } + return 0; - if (ret) - unregister_ip_vs_app(net, app); - +err_unreg: + unregister_ip_vs_app(net, app); +err_exit: + kfree(ipvs->ftp_app); return ret; } /* @@ -437,9 +447,10 @@ static int __net_init __ip_vs_ftp_init(struct net *net) */ static void __ip_vs_ftp_exit(struct net *net) { - struct ip_vs_app *app = &ip_vs_ftp; + struct netns_ipvs *ipvs = net_ipvs(net); - unregister_ip_vs_app(net, app); + unregister_ip_vs_app(net, ipvs->ftp_app); + kfree(ipvs->ftp_app); } static struct pernet_operations ip_vs_ftp_ops = { diff --git a/net/netfilter/ipvs/ip_vs_proto.c b/net/netfilter/ipvs/ip_vs_proto.c index 17484a4416e..eb86028536f 100644 --- a/net/netfilter/ipvs/ip_vs_proto.c +++ b/net/netfilter/ipvs/ip_vs_proto.c @@ -316,7 +316,7 @@ ip_vs_tcpudp_debug_packet(int af, struct ip_vs_protocol *pp, /* * per network name-space init */ -static int __net_init __ip_vs_protocol_init(struct net *net) +int __net_init __ip_vs_protocol_init(struct net *net) { #ifdef CONFIG_IP_VS_PROTO_TCP register_ip_vs_proto_netns(net, &ip_vs_protocol_tcp); @@ -336,7 +336,7 @@ static int __net_init __ip_vs_protocol_init(struct net *net) return 0; } -static void __net_exit __ip_vs_protocol_cleanup(struct net *net) +void __net_exit __ip_vs_protocol_cleanup(struct net *net) { struct netns_ipvs *ipvs = net_ipvs(net); struct ip_vs_proto_data *pd; @@ -349,11 +349,6 @@ static void __net_exit __ip_vs_protocol_cleanup(struct net *net) } } -static struct pernet_operations ipvs_proto_ops = { - .init = __ip_vs_protocol_init, - .exit = __ip_vs_protocol_cleanup, -}; - int __init ip_vs_protocol_init(void) { char protocols[64]; @@ -382,7 +377,6 @@ int __init ip_vs_protocol_init(void) REGISTER_PROTOCOL(&ip_vs_protocol_esp); #endif pr_info("Registered protocols (%s)\n", &protocols[2]); - return register_pernet_subsys(&ipvs_proto_ops); return 0; } @@ -393,7 +387,6 @@ void ip_vs_protocol_cleanup(void) struct ip_vs_protocol *pp; int i; - unregister_pernet_subsys(&ipvs_proto_ops); /* unregister all the ipvs protocols */ for (i = 0; i < IP_VS_PROTO_TAB_SIZE; i++) { while ((pp = ip_vs_proto_table[i]) != NULL) diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index 3e7961e85e9..e292e5bddc7 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -1303,13 +1303,18 @@ static struct socket *make_send_sock(struct net *net) struct socket *sock; int result; - /* First create a socket */ - result = __sock_create(net, PF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock, 1); + /* First create a socket move it to right name space later */ + result = sock_create_kern(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock); if (result < 0) { pr_err("Error during creation of socket; terminating\n"); return ERR_PTR(result); } - + /* + * Kernel sockets that are a part of a namespace, should not + * hold a reference to a namespace in order to allow to stop it. + * After sk_change_net should be released using sk_release_kernel. + */ + sk_change_net(sock->sk, net); result = set_mcast_if(sock->sk, ipvs->master_mcast_ifn); if (result < 0) { pr_err("Error setting outbound mcast interface\n"); @@ -1334,8 +1339,8 @@ static struct socket *make_send_sock(struct net *net) return sock; - error: - sock_release(sock); +error: + sk_release_kernel(sock->sk); return ERR_PTR(result); } @@ -1350,12 +1355,17 @@ static struct socket *make_receive_sock(struct net *net) int result; /* First create a socket */ - result = __sock_create(net, PF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock, 1); + result = sock_create_kern(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock); if (result < 0) { pr_err("Error during creation of socket; terminating\n"); return ERR_PTR(result); } - + /* + * Kernel sockets that are a part of a namespace, should not + * hold a reference to a namespace in order to allow to stop it. + * After sk_change_net should be released using sk_release_kernel. + */ + sk_change_net(sock->sk, net); /* it is equivalent to the REUSEADDR option in user-space */ sock->sk->sk_reuse = 1; @@ -1377,8 +1387,8 @@ static struct socket *make_receive_sock(struct net *net) return sock; - error: - sock_release(sock); +error: + sk_release_kernel(sock->sk); return ERR_PTR(result); } @@ -1473,7 +1483,7 @@ static int sync_thread_master(void *data) ip_vs_sync_buff_release(sb); /* release the sending multicast socket */ - sock_release(tinfo->sock); + sk_release_kernel(tinfo->sock->sk); kfree(tinfo); return 0; @@ -1513,7 +1523,7 @@ static int sync_thread_backup(void *data) } /* release the sending multicast socket */ - sock_release(tinfo->sock); + sk_release_kernel(tinfo->sock->sk); kfree(tinfo->buf); kfree(tinfo); @@ -1601,7 +1611,7 @@ outtinfo: outbuf: kfree(buf); outsocket: - sock_release(sock); + sk_release_kernel(sock->sk); out: return result; } @@ -1610,6 +1620,7 @@ out: int stop_sync_thread(struct net *net, int state) { struct netns_ipvs *ipvs = net_ipvs(net); + int retc = -EINVAL; IP_VS_DBG(7, "%s(): pid %d\n", __func__, task_pid_nr(current)); @@ -1629,7 +1640,7 @@ int stop_sync_thread(struct net *net, int state) spin_lock_bh(&ipvs->sync_lock); ipvs->sync_state &= ~IP_VS_STATE_MASTER; spin_unlock_bh(&ipvs->sync_lock); - kthread_stop(ipvs->master_thread); + retc = kthread_stop(ipvs->master_thread); ipvs->master_thread = NULL; } else if (state == IP_VS_STATE_BACKUP) { if (!ipvs->backup_thread) @@ -1639,22 +1650,20 @@ int stop_sync_thread(struct net *net, int state) task_pid_nr(ipvs->backup_thread)); ipvs->sync_state &= ~IP_VS_STATE_BACKUP; - kthread_stop(ipvs->backup_thread); + retc = kthread_stop(ipvs->backup_thread); ipvs->backup_thread = NULL; - } else { - return -EINVAL; } /* decrease the module use count */ ip_vs_use_count_dec(); - return 0; + return retc; } /* * Initialize data struct for each netns */ -static int __net_init __ip_vs_sync_init(struct net *net) +int __net_init __ip_vs_sync_init(struct net *net) { struct netns_ipvs *ipvs = net_ipvs(net); @@ -1668,24 +1677,24 @@ static int __net_init __ip_vs_sync_init(struct net *net) return 0; } -static void __ip_vs_sync_cleanup(struct net *net) +void __ip_vs_sync_cleanup(struct net *net) { - stop_sync_thread(net, IP_VS_STATE_MASTER); - stop_sync_thread(net, IP_VS_STATE_BACKUP); -} + int retc; -static struct pernet_operations ipvs_sync_ops = { - .init = __ip_vs_sync_init, - .exit = __ip_vs_sync_cleanup, -}; + retc = stop_sync_thread(net, IP_VS_STATE_MASTER); + if (retc && retc != -ESRCH) + pr_err("Failed to stop Master Daemon\n"); + retc = stop_sync_thread(net, IP_VS_STATE_BACKUP); + if (retc && retc != -ESRCH) + pr_err("Failed to stop Backup Daemon\n"); +} int __init ip_vs_sync_init(void) { - return register_pernet_subsys(&ipvs_sync_ops); + return 0; } void ip_vs_sync_cleanup(void) { - unregister_pernet_subsys(&ipvs_sync_ops); } diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 6132b213edd..ee319a4338b 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -87,7 +87,7 @@ __ip_vs_dst_check(struct ip_vs_dest *dest, u32 rtos) /* Get route to destination or remote server */ static struct rtable * __ip_vs_get_out_rt(struct sk_buff *skb, struct ip_vs_dest *dest, - __be32 daddr, u32 rtos, int rt_mode) + __be32 daddr, u32 rtos, int rt_mode, __be32 *ret_saddr) { struct net *net = dev_net(skb_dst(skb)->dev); struct rtable *rt; /* Route to the other host */ @@ -98,7 +98,12 @@ __ip_vs_get_out_rt(struct sk_buff *skb, struct ip_vs_dest *dest, spin_lock(&dest->dst_lock); if (!(rt = (struct rtable *) __ip_vs_dst_check(dest, rtos))) { - rt = ip_route_output(net, dest->addr.ip, 0, rtos, 0); + struct flowi4 fl4; + + memset(&fl4, 0, sizeof(fl4)); + fl4.daddr = dest->addr.ip; + fl4.flowi4_tos = rtos; + rt = ip_route_output_key(net, &fl4); if (IS_ERR(rt)) { spin_unlock(&dest->dst_lock); IP_VS_DBG_RL("ip_route_output error, dest: %pI4\n", @@ -106,18 +111,30 @@ __ip_vs_get_out_rt(struct sk_buff *skb, struct ip_vs_dest *dest, return NULL; } __ip_vs_dst_set(dest, rtos, dst_clone(&rt->dst), 0); - IP_VS_DBG(10, "new dst %pI4, refcnt=%d, rtos=%X\n", - &dest->addr.ip, + dest->dst_saddr.ip = fl4.saddr; + IP_VS_DBG(10, "new dst %pI4, src %pI4, refcnt=%d, " + "rtos=%X\n", + &dest->addr.ip, &dest->dst_saddr.ip, atomic_read(&rt->dst.__refcnt), rtos); } + daddr = dest->addr.ip; + if (ret_saddr) + *ret_saddr = dest->dst_saddr.ip; spin_unlock(&dest->dst_lock); } else { - rt = ip_route_output(net, daddr, 0, rtos, 0); + struct flowi4 fl4; + + memset(&fl4, 0, sizeof(fl4)); + fl4.daddr = daddr; + fl4.flowi4_tos = rtos; + rt = ip_route_output_key(net, &fl4); if (IS_ERR(rt)) { IP_VS_DBG_RL("ip_route_output error, dest: %pI4\n", &daddr); return NULL; } + if (ret_saddr) + *ret_saddr = fl4.saddr; } local = rt->rt_flags & RTCF_LOCAL; @@ -125,7 +142,7 @@ __ip_vs_get_out_rt(struct sk_buff *skb, struct ip_vs_dest *dest, rt_mode)) { IP_VS_DBG_RL("Stopping traffic to %s address, dest: %pI4\n", (rt->rt_flags & RTCF_LOCAL) ? - "local":"non-local", &rt->rt_dst); + "local":"non-local", &daddr); ip_rt_put(rt); return NULL; } @@ -133,14 +150,14 @@ __ip_vs_get_out_rt(struct sk_buff *skb, struct ip_vs_dest *dest, !((ort = skb_rtable(skb)) && ort->rt_flags & RTCF_LOCAL)) { IP_VS_DBG_RL("Redirect from non-local address %pI4 to local " "requires NAT method, dest: %pI4\n", - &ip_hdr(skb)->daddr, &rt->rt_dst); + &ip_hdr(skb)->daddr, &daddr); ip_rt_put(rt); return NULL; } if (unlikely(!local && ipv4_is_loopback(ip_hdr(skb)->saddr))) { IP_VS_DBG_RL("Stopping traffic from loopback address %pI4 " "to non-local address, dest: %pI4\n", - &ip_hdr(skb)->saddr, &rt->rt_dst); + &ip_hdr(skb)->saddr, &daddr); ip_rt_put(rt); return NULL; } @@ -229,8 +246,6 @@ out_err: /* * Get route to destination or remote server - * rt_mode: flags, &1=Allow local dest, &2=Allow non-local dest, - * &4=Allow redirect from remote daddr to local */ static struct rt6_info * __ip_vs_get_out_rt_v6(struct sk_buff *skb, struct ip_vs_dest *dest, @@ -250,7 +265,7 @@ __ip_vs_get_out_rt_v6(struct sk_buff *skb, struct ip_vs_dest *dest, u32 cookie; dst = __ip_vs_route_output_v6(net, &dest->addr.in6, - &dest->dst_saddr, + &dest->dst_saddr.in6, do_xfrm); if (!dst) { spin_unlock(&dest->dst_lock); @@ -260,11 +275,11 @@ __ip_vs_get_out_rt_v6(struct sk_buff *skb, struct ip_vs_dest *dest, cookie = rt->rt6i_node ? rt->rt6i_node->fn_sernum : 0; __ip_vs_dst_set(dest, 0, dst_clone(&rt->dst), cookie); IP_VS_DBG(10, "new dst %pI6, src %pI6, refcnt=%d\n", - &dest->addr.in6, &dest->dst_saddr, + &dest->addr.in6, &dest->dst_saddr.in6, atomic_read(&rt->dst.__refcnt)); } if (ret_saddr) - ipv6_addr_copy(ret_saddr, &dest->dst_saddr); + ipv6_addr_copy(ret_saddr, &dest->dst_saddr.in6); spin_unlock(&dest->dst_lock); } else { dst = __ip_vs_route_output_v6(net, daddr, ret_saddr, do_xfrm); @@ -274,13 +289,14 @@ __ip_vs_get_out_rt_v6(struct sk_buff *skb, struct ip_vs_dest *dest, } local = __ip_vs_is_local_route6(rt); - if (!((local ? 1 : 2) & rt_mode)) { + if (!((local ? IP_VS_RT_MODE_LOCAL : IP_VS_RT_MODE_NON_LOCAL) & + rt_mode)) { IP_VS_DBG_RL("Stopping traffic to %s address, dest: %pI6\n", local ? "local":"non-local", daddr); dst_release(&rt->dst); return NULL; } - if (local && !(rt_mode & 4) && + if (local && !(rt_mode & IP_VS_RT_MODE_RDR) && !((ort = (struct rt6_info *) skb_dst(skb)) && __ip_vs_is_local_route6(ort))) { IP_VS_DBG_RL("Redirect from non-local address %pI6 to local " @@ -386,7 +402,7 @@ ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, EnterFunction(10); if (!(rt = __ip_vs_get_out_rt(skb, NULL, iph->daddr, RT_TOS(iph->tos), - IP_VS_RT_MODE_NON_LOCAL))) + IP_VS_RT_MODE_NON_LOCAL, NULL))) goto tx_error_icmp; /* MTU checking */ @@ -440,7 +456,8 @@ ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, EnterFunction(10); - if (!(rt = __ip_vs_get_out_rt_v6(skb, NULL, &iph->daddr, NULL, 0, 2))) + if (!(rt = __ip_vs_get_out_rt_v6(skb, NULL, &iph->daddr, NULL, 0, + IP_VS_RT_MODE_NON_LOCAL))) goto tx_error_icmp; /* MTU checking */ @@ -517,7 +534,7 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, RT_TOS(iph->tos), IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | - IP_VS_RT_MODE_RDR))) + IP_VS_RT_MODE_RDR, NULL))) goto tx_error_icmp; local = rt->rt_flags & RTCF_LOCAL; /* @@ -539,7 +556,7 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, #endif /* From world but DNAT to loopback address? */ - if (local && ipv4_is_loopback(rt->rt_dst) && + if (local && ipv4_is_loopback(cp->daddr.ip) && rt_is_input_route(skb_rtable(skb))) { IP_VS_DBG_RL_PKT(1, AF_INET, pp, skb, 0, "ip_vs_nat_xmit(): " "stopping DNAT to loopback address"); @@ -632,7 +649,9 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, } if (!(rt = __ip_vs_get_out_rt_v6(skb, cp->dest, &cp->daddr.in6, NULL, - 0, 1|2|4))) + 0, (IP_VS_RT_MODE_LOCAL | + IP_VS_RT_MODE_NON_LOCAL | + IP_VS_RT_MODE_RDR)))) goto tx_error_icmp; local = __ip_vs_is_local_route6(rt); /* @@ -748,6 +767,7 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp) { struct rtable *rt; /* Route to the other host */ + __be32 saddr; /* Source for tunnel */ struct net_device *tdev; /* Device to other host */ struct iphdr *old_iph = ip_hdr(skb); u8 tos = old_iph->tos; @@ -761,7 +781,8 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, if (!(rt = __ip_vs_get_out_rt(skb, cp->dest, cp->daddr.ip, RT_TOS(tos), IP_VS_RT_MODE_LOCAL | - IP_VS_RT_MODE_NON_LOCAL))) + IP_VS_RT_MODE_NON_LOCAL, + &saddr))) goto tx_error_icmp; if (rt->rt_flags & RTCF_LOCAL) { ip_rt_put(rt); @@ -829,8 +850,8 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, iph->frag_off = df; iph->protocol = IPPROTO_IPIP; iph->tos = tos; - iph->daddr = rt->rt_dst; - iph->saddr = rt->rt_src; + iph->daddr = cp->daddr.ip; + iph->saddr = saddr; iph->ttl = old_iph->ttl; ip_select_ident(iph, &rt->dst, NULL); @@ -875,7 +896,8 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, EnterFunction(10); if (!(rt = __ip_vs_get_out_rt_v6(skb, cp->dest, &cp->daddr.in6, - &saddr, 1, 1|2))) + &saddr, 1, (IP_VS_RT_MODE_LOCAL | + IP_VS_RT_MODE_NON_LOCAL)))) goto tx_error_icmp; if (__ip_vs_is_local_route6(rt)) { dst_release(&rt->dst); @@ -992,7 +1014,7 @@ ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, if (!(rt = __ip_vs_get_out_rt(skb, cp->dest, cp->daddr.ip, RT_TOS(iph->tos), IP_VS_RT_MODE_LOCAL | - IP_VS_RT_MODE_NON_LOCAL))) + IP_VS_RT_MODE_NON_LOCAL, NULL))) goto tx_error_icmp; if (rt->rt_flags & RTCF_LOCAL) { ip_rt_put(rt); @@ -1050,7 +1072,8 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, EnterFunction(10); if (!(rt = __ip_vs_get_out_rt_v6(skb, cp->dest, &cp->daddr.in6, NULL, - 0, 1|2))) + 0, (IP_VS_RT_MODE_LOCAL | + IP_VS_RT_MODE_NON_LOCAL)))) goto tx_error_icmp; if (__ip_vs_is_local_route6(rt)) { dst_release(&rt->dst); @@ -1109,12 +1132,13 @@ tx_error: */ int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, int offset) + struct ip_vs_protocol *pp, int offset, unsigned int hooknum) { struct rtable *rt; /* Route to the other host */ int mtu; int rc; int local; + int rt_mode; EnterFunction(10); @@ -1135,11 +1159,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, * mangle and send the packet here (only for VS/NAT) */ + /* LOCALNODE from FORWARD hook is not supported */ + rt_mode = (hooknum != NF_INET_FORWARD) ? + IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | + IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL; if (!(rt = __ip_vs_get_out_rt(skb, cp->dest, cp->daddr.ip, RT_TOS(ip_hdr(skb)->tos), - IP_VS_RT_MODE_LOCAL | - IP_VS_RT_MODE_NON_LOCAL | - IP_VS_RT_MODE_RDR))) + rt_mode, NULL))) goto tx_error_icmp; local = rt->rt_flags & RTCF_LOCAL; @@ -1162,7 +1188,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, #endif /* From world but DNAT to loopback address? */ - if (local && ipv4_is_loopback(rt->rt_dst) && + if (local && ipv4_is_loopback(cp->daddr.ip) && rt_is_input_route(skb_rtable(skb))) { IP_VS_DBG(1, "%s(): " "stopping DNAT to loopback %pI4\n", @@ -1227,12 +1253,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, #ifdef CONFIG_IP_VS_IPV6 int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, int offset) + struct ip_vs_protocol *pp, int offset, unsigned int hooknum) { struct rt6_info *rt; /* Route to the other host */ int mtu; int rc; int local; + int rt_mode; EnterFunction(10); @@ -1253,8 +1280,12 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, * mangle and send the packet here (only for VS/NAT) */ + /* LOCALNODE from FORWARD hook is not supported */ + rt_mode = (hooknum != NF_INET_FORWARD) ? + IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | + IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL; if (!(rt = __ip_vs_get_out_rt_v6(skb, cp->dest, &cp->daddr.in6, NULL, - 0, 1|2|4))) + 0, rt_mode))) goto tx_error_icmp; local = __ip_vs_is_local_route6(rt); diff --git a/net/netfilter/nf_conntrack_extend.c b/net/netfilter/nf_conntrack_extend.c index 80a23ed62bb..05ecdc281a5 100644 --- a/net/netfilter/nf_conntrack_extend.c +++ b/net/netfilter/nf_conntrack_extend.c @@ -68,12 +68,6 @@ nf_ct_ext_create(struct nf_ct_ext **ext, enum nf_ct_ext_id id, gfp_t gfp) return (void *)(*ext) + off; } -static void __nf_ct_ext_free_rcu(struct rcu_head *head) -{ - struct nf_ct_ext *ext = container_of(head, struct nf_ct_ext, rcu); - kfree(ext); -} - void *__nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) { struct nf_ct_ext *old, *new; @@ -114,7 +108,7 @@ void *__nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) (void *)old + old->offset[i]); rcu_read_unlock(); } - call_rcu(&old->rcu, __nf_ct_ext_free_rcu); + kfree_rcu(old, rcu); ct->ext = new; } diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 30bf8a167fc..7dec88a1755 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -970,7 +970,7 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb, if (nlh->nlmsg_flags & NLM_F_DUMP) return netlink_dump_start(ctnl, skb, nlh, ctnetlink_dump_table, - ctnetlink_done); + ctnetlink_done, 0); err = ctnetlink_parse_zone(cda[CTA_ZONE], &zone); if (err < 0) @@ -1334,6 +1334,7 @@ ctnetlink_create_conntrack(struct net *net, u16 zone, struct nf_conn *ct; int err = -EINVAL; struct nf_conntrack_helper *helper; + struct nf_conn_tstamp *tstamp; ct = nf_conntrack_alloc(net, zone, otuple, rtuple, GFP_ATOMIC); if (IS_ERR(ct)) @@ -1451,6 +1452,9 @@ ctnetlink_create_conntrack(struct net *net, u16 zone, __set_bit(IPS_EXPECTED_BIT, &ct->status); ct->master = master_ct; } + tstamp = nf_conn_tstamp_find(ct); + if (tstamp) + tstamp->start = ktime_to_ns(ktime_get_real()); add_timer(&ct->timeout); nf_conntrack_hash_insert(ct); @@ -1836,7 +1840,7 @@ ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb, if (nlh->nlmsg_flags & NLM_F_DUMP) { return netlink_dump_start(ctnl, skb, nlh, ctnetlink_exp_dump_table, - ctnetlink_exp_done); + ctnetlink_exp_done, 0); } err = ctnetlink_parse_zone(cda[CTA_EXPECT_ZONE], &zone); diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index 237cc1981b8..cb5a2858178 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -1419,6 +1419,7 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, const char *dptr, *end; s16 diff, tdiff = 0; int ret = NF_ACCEPT; + bool term; typeof(nf_nat_sip_seq_adjust_hook) nf_nat_sip_seq_adjust; if (ctinfo != IP_CT_ESTABLISHED && @@ -1453,14 +1454,21 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, if (dptr + matchoff == end) break; - if (end + strlen("\r\n\r\n") > dptr + datalen) - break; - if (end[0] != '\r' || end[1] != '\n' || - end[2] != '\r' || end[3] != '\n') + term = false; + for (; end + strlen("\r\n\r\n") <= dptr + datalen; end++) { + if (end[0] == '\r' && end[1] == '\n' && + end[2] == '\r' && end[3] == '\n') { + term = true; + break; + } + } + if (!term) break; end += strlen("\r\n\r\n") + clen; msglen = origlen = end - dptr; + if (msglen > datalen) + return NF_DROP; ret = process_sip_msg(skb, ct, dataoff, &dptr, &msglen); if (ret != NF_ACCEPT) diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index 52959efca85..b0869fe3633 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -455,6 +455,7 @@ void xt_compat_flush_offsets(u_int8_t af) vfree(xt[af].compat_tab); xt[af].compat_tab = NULL; xt[af].number = 0; + xt[af].cur = 0; } } EXPORT_SYMBOL_GPL(xt_compat_flush_offsets); @@ -473,8 +474,7 @@ int xt_compat_calc_jump(u_int8_t af, unsigned int offset) else return mid ? tmp[mid - 1].delta : 0; } - WARN_ON_ONCE(1); - return 0; + return left ? tmp[left - 1].delta : 0; } EXPORT_SYMBOL_GPL(xt_compat_calc_jump); diff --git a/net/netfilter/xt_DSCP.c b/net/netfilter/xt_DSCP.c index 0a229191e55..ae8271652ef 100644 --- a/net/netfilter/xt_DSCP.c +++ b/net/netfilter/xt_DSCP.c @@ -99,7 +99,7 @@ tos_tg6(struct sk_buff *skb, const struct xt_action_param *par) u_int8_t orig, nv; orig = ipv6_get_dsfield(iph); - nv = (orig & info->tos_mask) ^ info->tos_value; + nv = (orig & ~info->tos_mask) ^ info->tos_value; if (orig != nv) { if (!skb_make_writable(skb, sizeof(struct iphdr))) diff --git a/net/netfilter/xt_conntrack.c b/net/netfilter/xt_conntrack.c index 481a86fdc40..61805d7b38a 100644 --- a/net/netfilter/xt_conntrack.c +++ b/net/netfilter/xt_conntrack.c @@ -272,11 +272,6 @@ static int conntrack_mt_check(const struct xt_mtchk_param *par) { int ret; - if (strcmp(par->table, "raw") == 0) { - pr_info("state is undetermined at the time of raw table\n"); - return -EINVAL; - } - ret = nf_ct_l3proto_try_module_get(par->family); if (ret < 0) pr_info("cannot load conntrack support for proto=%u\n", diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c index 4327e101c04..846f895cb65 100644 --- a/net/netfilter/xt_osf.c +++ b/net/netfilter/xt_osf.c @@ -62,13 +62,6 @@ static const struct nla_policy xt_osf_policy[OSF_ATTR_MAX + 1] = { [OSF_ATTR_FINGER] = { .len = sizeof(struct xt_osf_user_finger) }, }; -static void xt_osf_finger_free_rcu(struct rcu_head *rcu_head) -{ - struct xt_osf_finger *f = container_of(rcu_head, struct xt_osf_finger, rcu_head); - - kfree(f); -} - static int xt_osf_add_callback(struct sock *ctnl, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const osf_attrs[]) @@ -133,7 +126,7 @@ static int xt_osf_remove_callback(struct sock *ctnl, struct sk_buff *skb, * We are protected by nfnl mutex. */ list_del_rcu(&sf->finger_entry); - call_rcu(&sf->rcu_head, xt_osf_finger_free_rcu); + kfree_rcu(sf, rcu_head); err = 0; break; @@ -414,7 +407,7 @@ static void __exit xt_osf_fini(void) list_for_each_entry_rcu(f, &xt_osf_fingers[i], finger_entry) { list_del_rcu(&f->finger_entry); - call_rcu(&f->rcu_head, xt_osf_finger_free_rcu); + kfree_rcu(f, rcu_head); } } rcu_read_unlock(); diff --git a/net/netfilter/xt_set.c b/net/netfilter/xt_set.c index 061d48cec13..b3babaed771 100644 --- a/net/netfilter/xt_set.c +++ b/net/netfilter/xt_set.c @@ -81,6 +81,7 @@ set_match_v0_checkentry(const struct xt_mtchk_param *par) if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) { pr_warning("Protocol error: set match dimension " "is over the limit!\n"); + ip_set_nfnl_put(info->match_set.index); return -ERANGE; } @@ -135,6 +136,8 @@ set_target_v0_checkentry(const struct xt_tgchk_param *par) if (index == IPSET_INVALID_ID) { pr_warning("Cannot find del_set index %u as target\n", info->del_set.index); + if (info->add_set.index != IPSET_INVALID_ID) + ip_set_nfnl_put(info->add_set.index); return -ENOENT; } } @@ -142,6 +145,10 @@ set_target_v0_checkentry(const struct xt_tgchk_param *par) info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) { pr_warning("Protocol error: SET target dimension " "is over the limit!\n"); + if (info->add_set.index != IPSET_INVALID_ID) + ip_set_nfnl_put(info->add_set.index); + if (info->del_set.index != IPSET_INVALID_ID) + ip_set_nfnl_put(info->del_set.index); return -ERANGE; } @@ -192,6 +199,7 @@ set_match_checkentry(const struct xt_mtchk_param *par) if (info->match_set.dim > IPSET_DIM_MAX) { pr_warning("Protocol error: set match dimension " "is over the limit!\n"); + ip_set_nfnl_put(info->match_set.index); return -ERANGE; } @@ -219,7 +227,7 @@ set_target(struct sk_buff *skb, const struct xt_action_param *par) if (info->del_set.index != IPSET_INVALID_ID) ip_set_del(info->del_set.index, skb, par->family, - info->add_set.dim, + info->del_set.dim, info->del_set.flags); return XT_CONTINUE; @@ -245,13 +253,19 @@ set_target_checkentry(const struct xt_tgchk_param *par) if (index == IPSET_INVALID_ID) { pr_warning("Cannot find del_set index %u as target\n", info->del_set.index); + if (info->add_set.index != IPSET_INVALID_ID) + ip_set_nfnl_put(info->add_set.index); return -ENOENT; } } if (info->add_set.dim > IPSET_DIM_MAX || - info->del_set.flags > IPSET_DIM_MAX) { + info->del_set.dim > IPSET_DIM_MAX) { pr_warning("Protocol error: SET target dimension " "is over the limit!\n"); + if (info->add_set.index != IPSET_INVALID_ID) + ip_set_nfnl_put(info->add_set.index); + if (info->del_set.index != IPSET_INVALID_ID) + ip_set_nfnl_put(info->del_set.index); return -ERANGE; } diff --git a/net/netlabel/netlabel_addrlist.h b/net/netlabel/netlabel_addrlist.h index 1c1c093cf27..2b9644e19de 100644 --- a/net/netlabel/netlabel_addrlist.h +++ b/net/netlabel/netlabel_addrlist.h @@ -96,12 +96,12 @@ static inline struct netlbl_af4list *__af4list_valid_rcu(struct list_head *s, #define netlbl_af4list_foreach(iter, head) \ for (iter = __af4list_valid((head)->next, head); \ - prefetch(iter->list.next), &iter->list != (head); \ + &iter->list != (head); \ iter = __af4list_valid(iter->list.next, head)) #define netlbl_af4list_foreach_rcu(iter, head) \ for (iter = __af4list_valid_rcu((head)->next, head); \ - prefetch(iter->list.next), &iter->list != (head); \ + &iter->list != (head); \ iter = __af4list_valid_rcu(iter->list.next, head)) #define netlbl_af4list_foreach_safe(iter, tmp, head) \ @@ -163,12 +163,12 @@ static inline struct netlbl_af6list *__af6list_valid_rcu(struct list_head *s, #define netlbl_af6list_foreach(iter, head) \ for (iter = __af6list_valid((head)->next, head); \ - prefetch(iter->list.next), &iter->list != (head); \ + &iter->list != (head); \ iter = __af6list_valid(iter->list.next, head)) #define netlbl_af6list_foreach_rcu(iter, head) \ for (iter = __af6list_valid_rcu((head)->next, head); \ - prefetch(iter->list.next), &iter->list != (head); \ + &iter->list != (head); \ iter = __af6list_valid_rcu(iter->list.next, head)) #define netlbl_af6list_foreach_safe(iter, tmp, head) \ diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c index e2b0a680dd5..9c38658fba8 100644 --- a/net/netlabel/netlabel_unlabeled.c +++ b/net/netlabel/netlabel_unlabeled.c @@ -154,44 +154,6 @@ static const struct nla_policy netlbl_unlabel_genl_policy[NLBL_UNLABEL_A_MAX + 1 */ /** - * netlbl_unlhsh_free_addr4 - Frees an IPv4 address entry from the hash table - * @entry: the entry's RCU field - * - * Description: - * This function is designed to be used as a callback to the call_rcu() - * function so that memory allocated to a hash table address entry can be - * released safely. - * - */ -static void netlbl_unlhsh_free_addr4(struct rcu_head *entry) -{ - struct netlbl_unlhsh_addr4 *ptr; - - ptr = container_of(entry, struct netlbl_unlhsh_addr4, rcu); - kfree(ptr); -} - -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -/** - * netlbl_unlhsh_free_addr6 - Frees an IPv6 address entry from the hash table - * @entry: the entry's RCU field - * - * Description: - * This function is designed to be used as a callback to the call_rcu() - * function so that memory allocated to a hash table address entry can be - * released safely. - * - */ -static void netlbl_unlhsh_free_addr6(struct rcu_head *entry) -{ - struct netlbl_unlhsh_addr6 *ptr; - - ptr = container_of(entry, struct netlbl_unlhsh_addr6, rcu); - kfree(ptr); -} -#endif /* IPv6 */ - -/** * netlbl_unlhsh_free_iface - Frees an interface entry from the hash table * @entry: the entry's RCU field * @@ -568,7 +530,7 @@ static int netlbl_unlhsh_remove_addr4(struct net *net, if (entry == NULL) return -ENOENT; - call_rcu(&entry->rcu, netlbl_unlhsh_free_addr4); + kfree_rcu(entry, rcu); return 0; } @@ -629,7 +591,7 @@ static int netlbl_unlhsh_remove_addr6(struct net *net, if (entry == NULL) return -ENOENT; - call_rcu(&entry->rcu, netlbl_unlhsh_free_addr6); + kfree_rcu(entry, rcu); return 0; } #endif /* IPv6 */ diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index c8f35b5d2ee..0b92f75491b 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1566,12 +1566,6 @@ netlink_kernel_release(struct sock *sk) } EXPORT_SYMBOL(netlink_kernel_release); - -static void listeners_free_rcu(struct rcu_head *head) -{ - kfree(container_of(head, struct listeners, rcu)); -} - int __netlink_change_ngroups(struct sock *sk, unsigned int groups) { struct listeners *new, *old; @@ -1588,7 +1582,7 @@ int __netlink_change_ngroups(struct sock *sk, unsigned int groups) memcpy(new->masks, old->masks, NLGRPSZ(tbl->groups)); rcu_assign_pointer(tbl->listeners, new); - call_rcu(&old->rcu, listeners_free_rcu); + kfree_rcu(old, rcu); } tbl->groups = groups; @@ -1665,13 +1659,10 @@ static int netlink_dump(struct sock *sk) { struct netlink_sock *nlk = nlk_sk(sk); struct netlink_callback *cb; - struct sk_buff *skb; + struct sk_buff *skb = NULL; struct nlmsghdr *nlh; int len, err = -ENOBUFS; - - skb = sock_rmalloc(sk, NLMSG_GOODSIZE, 0, GFP_KERNEL); - if (!skb) - goto errout; + int alloc_size; mutex_lock(nlk->cb_mutex); @@ -1681,6 +1672,12 @@ static int netlink_dump(struct sock *sk) goto errout_skb; } + alloc_size = max_t(int, cb->min_dump_alloc, NLMSG_GOODSIZE); + + skb = sock_rmalloc(sk, alloc_size, 0, GFP_KERNEL); + if (!skb) + goto errout; + len = cb->dump(skb, cb); if (len > 0) { @@ -1727,7 +1724,8 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb, const struct nlmsghdr *nlh, int (*dump)(struct sk_buff *skb, struct netlink_callback *), - int (*done)(struct netlink_callback *)) + int (*done)(struct netlink_callback *), + u16 min_dump_alloc) { struct netlink_callback *cb; struct sock *sk; @@ -1741,6 +1739,7 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb, cb->dump = dump; cb->done = done; cb->nlh = nlh; + cb->min_dump_alloc = min_dump_alloc; atomic_inc(&skb->users); cb->skb = skb; @@ -1991,7 +1990,7 @@ static int netlink_seq_show(struct seq_file *seq, void *v) struct sock *s = v; struct netlink_sock *nlk = nlk_sk(s); - seq_printf(seq, "%p %-3d %-6d %08x %-8d %-8d %p %-8d %-8d %-8lu\n", + seq_printf(seq, "%pK %-3d %-6d %08x %-8d %-8d %pK %-8d %-8d %-8lu\n", s, s->sk_protocol, nlk->pid, diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 1781d99145e..482fa571b4e 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -525,7 +525,7 @@ static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) genl_unlock(); err = netlink_dump_start(net->genl_sock, skb, nlh, - ops->dumpit, ops->done); + ops->dumpit, ops->done, 0); genl_lock(); return err; } diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index b5362e96022..b54ec41adea 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -538,7 +538,7 @@ static inline unsigned int run_filter(const struct sk_buff *skb, rcu_read_lock(); filter = rcu_dereference(sk->sk_filter); if (filter != NULL) - res = sk_run_filter(skb, filter->insns); + res = SK_RUN_FILTER(filter, skb); rcu_read_unlock(); return res; @@ -798,7 +798,12 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, getnstimeofday(&ts); h.h2->tp_sec = ts.tv_sec; h.h2->tp_nsec = ts.tv_nsec; - h.h2->tp_vlan_tci = vlan_tx_tag_get(skb); + if (vlan_tx_tag_present(skb)) { + h.h2->tp_vlan_tci = vlan_tx_tag_get(skb); + status |= TP_STATUS_VLAN_VALID; + } else { + h.h2->tp_vlan_tci = 0; + } hdrlen = sizeof(*h.h2); break; default: @@ -969,7 +974,8 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) struct sk_buff *skb; struct net_device *dev; __be16 proto; - int ifindex, err, reserve = 0; + bool need_rls_dev = false; + int err, reserve = 0; void *ph; struct sockaddr_ll *saddr = (struct sockaddr_ll *)msg->msg_name; int tp_len, size_max; @@ -981,7 +987,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) err = -EBUSY; if (saddr == NULL) { - ifindex = po->ifindex; + dev = po->prot_hook.dev; proto = po->num; addr = NULL; } else { @@ -992,12 +998,12 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) + offsetof(struct sockaddr_ll, sll_addr))) goto out; - ifindex = saddr->sll_ifindex; proto = saddr->sll_protocol; addr = saddr->sll_addr; + dev = dev_get_by_index(sock_net(&po->sk), saddr->sll_ifindex); + need_rls_dev = true; } - dev = dev_get_by_index(sock_net(&po->sk), ifindex); err = -ENXIO; if (unlikely(dev == NULL)) goto out; @@ -1083,7 +1089,8 @@ out_status: __packet_set_status(po, ph, status); kfree_skb(skb); out_put: - dev_put(dev); + if (need_rls_dev) + dev_put(dev); out: mutex_unlock(&po->pg_vec_lock); return err; @@ -1121,8 +1128,9 @@ static int packet_snd(struct socket *sock, struct sk_buff *skb; struct net_device *dev; __be16 proto; + bool need_rls_dev = false; unsigned char *addr; - int ifindex, err, reserve = 0; + int err, reserve = 0; struct virtio_net_hdr vnet_hdr = { 0 }; int offset = 0; int vnet_hdr_len; @@ -1134,7 +1142,7 @@ static int packet_snd(struct socket *sock, */ if (saddr == NULL) { - ifindex = po->ifindex; + dev = po->prot_hook.dev; proto = po->num; addr = NULL; } else { @@ -1143,13 +1151,12 @@ static int packet_snd(struct socket *sock, goto out; if (msg->msg_namelen < (saddr->sll_halen + offsetof(struct sockaddr_ll, sll_addr))) goto out; - ifindex = saddr->sll_ifindex; proto = saddr->sll_protocol; addr = saddr->sll_addr; + dev = dev_get_by_index(sock_net(sk), saddr->sll_ifindex); + need_rls_dev = true; } - - dev = dev_get_by_index(sock_net(sk), ifindex); err = -ENXIO; if (dev == NULL) goto out_unlock; @@ -1280,14 +1287,15 @@ static int packet_snd(struct socket *sock, if (err > 0 && (err = net_xmit_errno(err)) != 0) goto out_unlock; - dev_put(dev); + if (need_rls_dev) + dev_put(dev); return len; out_free: kfree_skb(skb); out_unlock: - if (dev) + if (dev && need_rls_dev) dev_put(dev); out: return err; @@ -1337,6 +1345,10 @@ static int packet_release(struct socket *sock) __dev_remove_pack(&po->prot_hook); __sock_put(sk); } + if (po->prot_hook.dev) { + dev_put(po->prot_hook.dev); + po->prot_hook.dev = NULL; + } spin_unlock(&po->bind_lock); packet_flush_mclist(sk); @@ -1390,6 +1402,8 @@ static int packet_do_bind(struct sock *sk, struct net_device *dev, __be16 protoc po->num = protocol; po->prot_hook.type = protocol; + if (po->prot_hook.dev) + dev_put(po->prot_hook.dev); po->prot_hook.dev = dev; po->ifindex = dev ? dev->ifindex : 0; @@ -1434,10 +1448,8 @@ static int packet_bind_spkt(struct socket *sock, struct sockaddr *uaddr, strlcpy(name, uaddr->sa_data, sizeof(name)); dev = dev_get_by_name(sock_net(sk), name); - if (dev) { + if (dev) err = packet_do_bind(sk, dev, pkt_sk(sk)->num); - dev_put(dev); - } return err; } @@ -1465,8 +1477,6 @@ static int packet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len goto out; } err = packet_do_bind(sk, dev, sll->sll_protocol ? : pkt_sk(sk)->num); - if (dev) - dev_put(dev); out: return err; @@ -1675,6 +1685,8 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock, vnet_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; vnet_hdr.csum_start = skb_checksum_start_offset(skb); vnet_hdr.csum_offset = skb->csum_offset; + } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) { + vnet_hdr.flags = VIRTIO_NET_HDR_F_DATA_VALID; } /* else everything is zero */ err = memcpy_toiovec(msg->msg_iov, (void *)&vnet_hdr, @@ -1725,8 +1737,12 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock, aux.tp_snaplen = skb->len; aux.tp_mac = 0; aux.tp_net = skb_network_offset(skb); - aux.tp_vlan_tci = vlan_tx_tag_get(skb); - + if (vlan_tx_tag_present(skb)) { + aux.tp_vlan_tci = vlan_tx_tag_get(skb); + aux.tp_status |= TP_STATUS_VLAN_VALID; + } else { + aux.tp_vlan_tci = 0; + } put_cmsg(msg, SOL_PACKET, PACKET_AUXDATA, sizeof(aux), &aux); } @@ -2231,6 +2247,8 @@ static int packet_notifier(struct notifier_block *this, unsigned long msg, void } if (msg == NETDEV_UNREGISTER) { po->ifindex = -1; + if (po->prot_hook.dev) + dev_put(po->prot_hook.dev); po->prot_hook.dev = NULL; } spin_unlock(&po->bind_lock); @@ -2706,7 +2724,7 @@ static int packet_seq_show(struct seq_file *seq, void *v) const struct packet_sock *po = pkt_sk(s); seq_printf(seq, - "%p %-6d %-4d %04x %-5d %1d %-6u %-6u %-6lu\n", + "%pK %-6d %-4d %04x %-5d %1d %-6u %-6u %-6lu\n", s, atomic_read(&s->sk_refcnt), s->sk_type, diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c index 947038ddd04..d2df8f33160 100644 --- a/net/phonet/pn_dev.c +++ b/net/phonet/pn_dev.c @@ -162,14 +162,6 @@ int phonet_address_add(struct net_device *dev, u8 addr) return err; } -static void phonet_device_rcu_free(struct rcu_head *head) -{ - struct phonet_device *pnd; - - pnd = container_of(head, struct phonet_device, rcu); - kfree(pnd); -} - int phonet_address_del(struct net_device *dev, u8 addr) { struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); @@ -188,7 +180,7 @@ int phonet_address_del(struct net_device *dev, u8 addr) mutex_unlock(&pndevs->lock); if (pnd) - call_rcu(&pnd->rcu, phonet_device_rcu_free); + kfree_rcu(pnd, rcu); return err; } @@ -426,18 +418,14 @@ int phonet_route_del(struct net_device *dev, u8 daddr) return 0; } -struct net_device *phonet_route_get(struct net *net, u8 daddr) +struct net_device *phonet_route_get_rcu(struct net *net, u8 daddr) { struct phonet_net *pnn = phonet_pernet(net); struct phonet_routes *routes = &pnn->routes; struct net_device *dev; - ASSERT_RTNL(); /* no need to hold the device */ - daddr >>= 2; - rcu_read_lock(); dev = rcu_dereference(routes->table[daddr]); - rcu_read_unlock(); return dev; } diff --git a/net/phonet/pn_netlink.c b/net/phonet/pn_netlink.c index 58b3b1f991e..d61f6761777 100644 --- a/net/phonet/pn_netlink.c +++ b/net/phonet/pn_netlink.c @@ -264,10 +264,11 @@ static int route_dumpit(struct sk_buff *skb, struct netlink_callback *cb) struct net *net = sock_net(skb->sk); u8 addr, addr_idx = 0, addr_start_idx = cb->args[0]; + rcu_read_lock(); for (addr = 0; addr < 64; addr++) { struct net_device *dev; - dev = phonet_route_get(net, addr << 2); + dev = phonet_route_get_rcu(net, addr << 2); if (!dev) continue; @@ -279,6 +280,7 @@ static int route_dumpit(struct sk_buff *skb, struct netlink_callback *cb) } out: + rcu_read_unlock(); cb->args[0] = addr_idx; cb->args[1] = 0; @@ -287,15 +289,16 @@ out: int __init phonet_netlink_register(void) { - int err = __rtnl_register(PF_PHONET, RTM_NEWADDR, addr_doit, NULL); + int err = __rtnl_register(PF_PHONET, RTM_NEWADDR, addr_doit, + NULL, NULL); if (err) return err; /* Further __rtnl_register() cannot fail */ - __rtnl_register(PF_PHONET, RTM_DELADDR, addr_doit, NULL); - __rtnl_register(PF_PHONET, RTM_GETADDR, NULL, getaddr_dumpit); - __rtnl_register(PF_PHONET, RTM_NEWROUTE, route_doit, NULL); - __rtnl_register(PF_PHONET, RTM_DELROUTE, route_doit, NULL); - __rtnl_register(PF_PHONET, RTM_GETROUTE, NULL, route_dumpit); + __rtnl_register(PF_PHONET, RTM_DELADDR, addr_doit, NULL, NULL); + __rtnl_register(PF_PHONET, RTM_GETADDR, NULL, getaddr_dumpit, NULL); + __rtnl_register(PF_PHONET, RTM_NEWROUTE, route_doit, NULL, NULL); + __rtnl_register(PF_PHONET, RTM_DELROUTE, route_doit, NULL, NULL); + __rtnl_register(PF_PHONET, RTM_GETROUTE, NULL, route_dumpit, NULL); return 0; } diff --git a/net/phonet/socket.c b/net/phonet/socket.c index 8c5bfcef92c..ab07711cf2f 100644 --- a/net/phonet/socket.c +++ b/net/phonet/socket.c @@ -607,7 +607,7 @@ static int pn_sock_seq_show(struct seq_file *seq, void *v) struct pn_sock *pn = pn_sk(sk); seq_printf(seq, "%2d %04X:%04X:%02X %02X %08X:%08X %5d %lu " - "%d %p %d%n", + "%d %pK %d%n", sk->sk_protocol, pn->sobject, pn->dobject, pn->resource, sk->sk_state, sk_wmem_alloc_get(sk), sk_rmem_alloc_get(sk), diff --git a/net/rds/ib.c b/net/rds/ib.c index cce19f95c62..3b83086bcc3 100644 --- a/net/rds/ib.c +++ b/net/rds/ib.c @@ -325,7 +325,7 @@ static int rds_ib_laddr_check(__be32 addr) /* Create a CMA ID and try to bind it. This catches both * IB and iWARP capable NICs. */ - cm_id = rdma_create_id(NULL, NULL, RDMA_PS_TCP); + cm_id = rdma_create_id(NULL, NULL, RDMA_PS_TCP, IB_QPT_RC); if (IS_ERR(cm_id)) return PTR_ERR(cm_id); diff --git a/net/rds/ib.h b/net/rds/ib.h index 4297d92788d..edfaaaf164e 100644 --- a/net/rds/ib.h +++ b/net/rds/ib.h @@ -3,6 +3,7 @@ #include <rdma/ib_verbs.h> #include <rdma/rdma_cm.h> +#include <linux/interrupt.h> #include <linux/pci.h> #include <linux/slab.h> #include "rds.h" diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c index ee369d201a6..fd453dd5124 100644 --- a/net/rds/ib_cm.c +++ b/net/rds/ib_cm.c @@ -587,7 +587,7 @@ int rds_ib_conn_connect(struct rds_connection *conn) /* XXX I wonder what affect the port space has */ /* delegate cm event handler to rdma_transport */ ic->i_cm_id = rdma_create_id(rds_rdma_cm_event_handler, conn, - RDMA_PS_TCP); + RDMA_PS_TCP, IB_QPT_RC); if (IS_ERR(ic->i_cm_id)) { ret = PTR_ERR(ic->i_cm_id); ic->i_cm_id = NULL; diff --git a/net/rds/iw.c b/net/rds/iw.c index 5a9676fe594..f7474844f09 100644 --- a/net/rds/iw.c +++ b/net/rds/iw.c @@ -226,7 +226,7 @@ static int rds_iw_laddr_check(__be32 addr) /* Create a CMA ID and try to bind it. This catches both * IB and iWARP capable NICs. */ - cm_id = rdma_create_id(NULL, NULL, RDMA_PS_TCP); + cm_id = rdma_create_id(NULL, NULL, RDMA_PS_TCP, IB_QPT_RC); if (IS_ERR(cm_id)) return PTR_ERR(cm_id); diff --git a/net/rds/iw.h b/net/rds/iw.h index 90151922178..04ce3b193f7 100644 --- a/net/rds/iw.h +++ b/net/rds/iw.h @@ -1,6 +1,7 @@ #ifndef _RDS_IW_H #define _RDS_IW_H +#include <linux/interrupt.h> #include <rdma/ib_verbs.h> #include <rdma/rdma_cm.h> #include "rds.h" diff --git a/net/rds/iw_cm.c b/net/rds/iw_cm.c index 3a60a15d1b4..c12db66f24c 100644 --- a/net/rds/iw_cm.c +++ b/net/rds/iw_cm.c @@ -522,7 +522,7 @@ int rds_iw_conn_connect(struct rds_connection *conn) /* XXX I wonder what affect the port space has */ /* delegate cm event handler to rdma_transport */ ic->i_cm_id = rdma_create_id(rds_rdma_cm_event_handler, conn, - RDMA_PS_TCP); + RDMA_PS_TCP, IB_QPT_RC); if (IS_ERR(ic->i_cm_id)) { ret = PTR_ERR(ic->i_cm_id); ic->i_cm_id = NULL; diff --git a/net/rds/rdma_transport.c b/net/rds/rdma_transport.c index 4195a053982..f8760e1b668 100644 --- a/net/rds/rdma_transport.c +++ b/net/rds/rdma_transport.c @@ -158,7 +158,8 @@ static int rds_rdma_listen_init(void) struct rdma_cm_id *cm_id; int ret; - cm_id = rdma_create_id(rds_rdma_cm_event_handler, NULL, RDMA_PS_TCP); + cm_id = rdma_create_id(rds_rdma_cm_event_handler, NULL, RDMA_PS_TCP, + IB_QPT_RC); if (IS_ERR(cm_id)) { ret = PTR_ERR(cm_id); printk(KERN_ERR "RDS/RDMA: failed to setup listener, " diff --git a/net/rfkill/Kconfig b/net/rfkill/Kconfig index 7fce6dfd218..78efe895b66 100644 --- a/net/rfkill/Kconfig +++ b/net/rfkill/Kconfig @@ -22,3 +22,23 @@ config RFKILL_INPUT depends on RFKILL depends on INPUT = y || RFKILL = INPUT default y if !EXPERT + +config RFKILL_REGULATOR + tristate "Generic rfkill regulator driver" + depends on RFKILL || !RFKILL + depends on REGULATOR + help + This options enable controlling radio transmitters connected to + voltage regulator using the regulator framework. + + To compile this driver as a module, choose M here: the module will + be called rfkill-regulator. + +config RFKILL_GPIO + tristate "GPIO RFKILL driver" + depends on RFKILL && GPIOLIB && HAVE_CLK + default n + help + If you say yes here you get support of a generic gpio RFKILL + driver. The platform should fill in the appropriate fields in the + rfkill_gpio_platform_data structure and pass that to the driver. diff --git a/net/rfkill/Makefile b/net/rfkill/Makefile index 66210535269..311768783f4 100644 --- a/net/rfkill/Makefile +++ b/net/rfkill/Makefile @@ -5,3 +5,5 @@ rfkill-y += core.o rfkill-$(CONFIG_RFKILL_INPUT) += input.o obj-$(CONFIG_RFKILL) += rfkill.o +obj-$(CONFIG_RFKILL_REGULATOR) += rfkill-regulator.o +obj-$(CONFIG_RFKILL_GPIO) += rfkill-gpio.o diff --git a/net/rfkill/core.c b/net/rfkill/core.c index 0198191b756..be90640a277 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -1024,7 +1024,6 @@ static int rfkill_fop_open(struct inode *inode, struct file *file) * start getting events from elsewhere but hold mtx to get * startup events added first */ - list_add(&data->list, &rfkill_fds); list_for_each_entry(rfkill, &rfkill_list, node) { ev = kzalloc(sizeof(*ev), GFP_KERNEL); @@ -1033,6 +1032,7 @@ static int rfkill_fop_open(struct inode *inode, struct file *file) rfkill_fill_event(&ev->ev, rfkill, RFKILL_OP_ADD); list_add_tail(&ev->list, &data->events); } + list_add(&data->list, &rfkill_fds); mutex_unlock(&data->mtx); mutex_unlock(&rfkill_global_mutex); diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c new file mode 100644 index 00000000000..256c5ddd2d7 --- /dev/null +++ b/net/rfkill/rfkill-gpio.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2011, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/gpio.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/rfkill.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/slab.h> + +#include <linux/rfkill-gpio.h> + +enum rfkill_gpio_clk_state { + UNSPECIFIED = 0, + PWR_ENABLED, + PWR_DISABLED +}; + +#define PWR_CLK_SET(_RF, _EN) \ + ((_RF)->pwr_clk_enabled = (!(_EN) ? PWR_ENABLED : PWR_DISABLED)) +#define PWR_CLK_ENABLED(_RF) ((_RF)->pwr_clk_enabled == PWR_ENABLED) +#define PWR_CLK_DISABLED(_RF) ((_RF)->pwr_clk_enabled != PWR_ENABLED) + +struct rfkill_gpio_data { + struct rfkill_gpio_platform_data *pdata; + struct rfkill *rfkill_dev; + char *reset_name; + char *shutdown_name; + enum rfkill_gpio_clk_state pwr_clk_enabled; + struct clk *pwr_clk; +}; + +static int rfkill_gpio_set_power(void *data, bool blocked) +{ + struct rfkill_gpio_data *rfkill = data; + + if (blocked) { + if (gpio_is_valid(rfkill->pdata->shutdown_gpio)) + gpio_direction_output(rfkill->pdata->shutdown_gpio, 0); + if (gpio_is_valid(rfkill->pdata->reset_gpio)) + gpio_direction_output(rfkill->pdata->reset_gpio, 0); + if (rfkill->pwr_clk && PWR_CLK_ENABLED(rfkill)) + clk_disable(rfkill->pwr_clk); + } else { + if (rfkill->pwr_clk && PWR_CLK_DISABLED(rfkill)) + clk_enable(rfkill->pwr_clk); + if (gpio_is_valid(rfkill->pdata->reset_gpio)) + gpio_direction_output(rfkill->pdata->reset_gpio, 1); + if (gpio_is_valid(rfkill->pdata->shutdown_gpio)) + gpio_direction_output(rfkill->pdata->shutdown_gpio, 1); + } + + if (rfkill->pwr_clk) + PWR_CLK_SET(rfkill, blocked); + + return 0; +} + +static const struct rfkill_ops rfkill_gpio_ops = { + .set_block = rfkill_gpio_set_power, +}; + +static int rfkill_gpio_probe(struct platform_device *pdev) +{ + struct rfkill_gpio_data *rfkill; + struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data; + int ret = 0; + int len = 0; + + if (!pdata) { + pr_warn("%s: No platform data specified\n", __func__); + return -EINVAL; + } + + /* make sure at-least one of the GPIO is defined and that + * a name is specified for this instance */ + if (!pdata->name || (!gpio_is_valid(pdata->reset_gpio) && + !gpio_is_valid(pdata->shutdown_gpio))) { + pr_warn("%s: invalid platform data\n", __func__); + return -EINVAL; + } + + rfkill = kzalloc(sizeof(*rfkill), GFP_KERNEL); + if (!rfkill) + return -ENOMEM; + + rfkill->pdata = pdata; + + len = strlen(pdata->name); + rfkill->reset_name = kzalloc(len + 7, GFP_KERNEL); + if (!rfkill->reset_name) { + ret = -ENOMEM; + goto fail_alloc; + } + + rfkill->shutdown_name = kzalloc(len + 10, GFP_KERNEL); + if (!rfkill->shutdown_name) { + ret = -ENOMEM; + goto fail_reset_name; + } + + snprintf(rfkill->reset_name, len + 6 , "%s_reset", pdata->name); + snprintf(rfkill->shutdown_name, len + 9, "%s_shutdown", pdata->name); + + if (pdata->power_clk_name) { + rfkill->pwr_clk = clk_get(&pdev->dev, pdata->power_clk_name); + if (IS_ERR(rfkill->pwr_clk)) { + pr_warn("%s: can't find pwr_clk.\n", __func__); + goto fail_shutdown_name; + } + } + + if (gpio_is_valid(pdata->reset_gpio)) { + ret = gpio_request(pdata->reset_gpio, rfkill->reset_name); + if (ret) { + pr_warn("%s: failed to get reset gpio.\n", __func__); + goto fail_clock; + } + } + + if (gpio_is_valid(pdata->shutdown_gpio)) { + ret = gpio_request(pdata->shutdown_gpio, rfkill->shutdown_name); + if (ret) { + pr_warn("%s: failed to get shutdown gpio.\n", __func__); + goto fail_reset; + } + } + + rfkill->rfkill_dev = rfkill_alloc(pdata->name, &pdev->dev, pdata->type, + &rfkill_gpio_ops, rfkill); + if (!rfkill->rfkill_dev) + goto fail_shutdown; + + ret = rfkill_register(rfkill->rfkill_dev); + if (ret < 0) + goto fail_rfkill; + + platform_set_drvdata(pdev, rfkill); + + dev_info(&pdev->dev, "%s device registered.\n", pdata->name); + + return 0; + +fail_rfkill: + rfkill_destroy(rfkill->rfkill_dev); +fail_shutdown: + if (gpio_is_valid(pdata->shutdown_gpio)) + gpio_free(pdata->shutdown_gpio); +fail_reset: + if (gpio_is_valid(pdata->reset_gpio)) + gpio_free(pdata->reset_gpio); +fail_clock: + if (rfkill->pwr_clk) + clk_put(rfkill->pwr_clk); +fail_shutdown_name: + kfree(rfkill->shutdown_name); +fail_reset_name: + kfree(rfkill->reset_name); +fail_alloc: + kfree(rfkill); + + return ret; +} + +static int rfkill_gpio_remove(struct platform_device *pdev) +{ + struct rfkill_gpio_data *rfkill = platform_get_drvdata(pdev); + + rfkill_unregister(rfkill->rfkill_dev); + rfkill_destroy(rfkill->rfkill_dev); + if (gpio_is_valid(rfkill->pdata->shutdown_gpio)) + gpio_free(rfkill->pdata->shutdown_gpio); + if (gpio_is_valid(rfkill->pdata->reset_gpio)) + gpio_free(rfkill->pdata->reset_gpio); + if (rfkill->pwr_clk && PWR_CLK_ENABLED(rfkill)) + clk_disable(rfkill->pwr_clk); + if (rfkill->pwr_clk) + clk_put(rfkill->pwr_clk); + kfree(rfkill->shutdown_name); + kfree(rfkill->reset_name); + kfree(rfkill); + + return 0; +} + +static struct platform_driver rfkill_gpio_driver = { + .probe = rfkill_gpio_probe, + .remove = __devexit_p(rfkill_gpio_remove), + .driver = { + .name = "rfkill_gpio", + .owner = THIS_MODULE, + }, +}; + +static int __init rfkill_gpio_init(void) +{ + return platform_driver_register(&rfkill_gpio_driver); +} + +static void __exit rfkill_gpio_exit(void) +{ + platform_driver_unregister(&rfkill_gpio_driver); +} + +module_init(rfkill_gpio_init); +module_exit(rfkill_gpio_exit); + +MODULE_DESCRIPTION("gpio rfkill"); +MODULE_AUTHOR("NVIDIA"); +MODULE_LICENSE("GPL"); diff --git a/net/rfkill/rfkill-regulator.c b/net/rfkill/rfkill-regulator.c new file mode 100644 index 00000000000..18dc512a10f --- /dev/null +++ b/net/rfkill/rfkill-regulator.c @@ -0,0 +1,164 @@ +/* + * rfkill-regulator.c - Regulator consumer driver for rfkill + * + * Copyright (C) 2009 Guiming Zhuo <gmzhuo@gmail.com> + * Copyright (C) 2011 Antonio Ospite <ospite@studenti.unina.it> + * + * Implementation inspired by leds-regulator driver. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/module.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/rfkill.h> +#include <linux/rfkill-regulator.h> + +struct rfkill_regulator_data { + struct rfkill *rf_kill; + bool reg_enabled; + + struct regulator *vcc; +}; + +static int rfkill_regulator_set_block(void *data, bool blocked) +{ + struct rfkill_regulator_data *rfkill_data = data; + + pr_debug("%s: blocked: %d\n", __func__, blocked); + + if (blocked) { + if (rfkill_data->reg_enabled) { + regulator_disable(rfkill_data->vcc); + rfkill_data->reg_enabled = 0; + } + } else { + if (!rfkill_data->reg_enabled) { + regulator_enable(rfkill_data->vcc); + rfkill_data->reg_enabled = 1; + } + } + + pr_debug("%s: regulator_is_enabled after set_block: %d\n", __func__, + regulator_is_enabled(rfkill_data->vcc)); + + return 0; +} + +struct rfkill_ops rfkill_regulator_ops = { + .set_block = rfkill_regulator_set_block, +}; + +static int __devinit rfkill_regulator_probe(struct platform_device *pdev) +{ + struct rfkill_regulator_platform_data *pdata = pdev->dev.platform_data; + struct rfkill_regulator_data *rfkill_data; + struct regulator *vcc; + struct rfkill *rf_kill; + int ret = 0; + + if (pdata == NULL) { + dev_err(&pdev->dev, "no platform data\n"); + return -ENODEV; + } + + if (pdata->name == NULL || pdata->type == 0) { + dev_err(&pdev->dev, "invalid name or type in platform data\n"); + return -EINVAL; + } + + vcc = regulator_get_exclusive(&pdev->dev, "vrfkill"); + if (IS_ERR(vcc)) { + dev_err(&pdev->dev, "Cannot get vcc for %s\n", pdata->name); + ret = PTR_ERR(vcc); + goto out; + } + + rfkill_data = kzalloc(sizeof(*rfkill_data), GFP_KERNEL); + if (rfkill_data == NULL) { + ret = -ENOMEM; + goto err_data_alloc; + } + + rf_kill = rfkill_alloc(pdata->name, &pdev->dev, + pdata->type, + &rfkill_regulator_ops, rfkill_data); + if (rf_kill == NULL) { + dev_err(&pdev->dev, "Cannot alloc rfkill device\n"); + ret = -ENOMEM; + goto err_rfkill_alloc; + } + + if (regulator_is_enabled(vcc)) { + dev_dbg(&pdev->dev, "Regulator already enabled\n"); + rfkill_data->reg_enabled = 1; + } + rfkill_data->vcc = vcc; + rfkill_data->rf_kill = rf_kill; + + ret = rfkill_register(rf_kill); + if (ret) { + dev_err(&pdev->dev, "Cannot register rfkill device\n"); + goto err_rfkill_register; + } + + platform_set_drvdata(pdev, rfkill_data); + dev_info(&pdev->dev, "%s initialized\n", pdata->name); + + return 0; + +err_rfkill_register: + rfkill_destroy(rf_kill); +err_rfkill_alloc: + kfree(rfkill_data); +err_data_alloc: + regulator_put(vcc); +out: + return ret; +} + +static int __devexit rfkill_regulator_remove(struct platform_device *pdev) +{ + struct rfkill_regulator_data *rfkill_data = platform_get_drvdata(pdev); + struct rfkill *rf_kill = rfkill_data->rf_kill; + + rfkill_unregister(rf_kill); + rfkill_destroy(rf_kill); + regulator_put(rfkill_data->vcc); + kfree(rfkill_data); + + return 0; +} + +static struct platform_driver rfkill_regulator_driver = { + .probe = rfkill_regulator_probe, + .remove = __devexit_p(rfkill_regulator_remove), + .driver = { + .name = "rfkill-regulator", + .owner = THIS_MODULE, + }, +}; + +static int __init rfkill_regulator_init(void) +{ + return platform_driver_register(&rfkill_regulator_driver); +} +module_init(rfkill_regulator_init); + +static void __exit rfkill_regulator_exit(void) +{ + platform_driver_unregister(&rfkill_regulator_driver); +} +module_exit(rfkill_regulator_exit); + +MODULE_AUTHOR("Guiming Zhuo <gmzhuo@gmail.com>"); +MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>"); +MODULE_DESCRIPTION("Regulator consumer driver for rfkill"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rfkill-regulator"); diff --git a/net/rxrpc/ar-ack.c b/net/rxrpc/ar-ack.c index b6ffe4e1b84..f99cfce7ca9 100644 --- a/net/rxrpc/ar-ack.c +++ b/net/rxrpc/ar-ack.c @@ -375,7 +375,6 @@ protocol_error: */ static void rxrpc_rotate_tx_window(struct rxrpc_call *call, u32 hard) { - struct rxrpc_skb_priv *sp; unsigned long _skb; int tail = call->acks_tail, old_tail; int win = CIRC_CNT(call->acks_head, tail, call->acks_winsz); @@ -387,7 +386,6 @@ static void rxrpc_rotate_tx_window(struct rxrpc_call *call, u32 hard) while (call->acks_hard < hard) { smp_read_barrier_depends(); _skb = call->acks_window[tail] & ~1; - sp = rxrpc_skb((struct sk_buff *) _skb); rxrpc_free_skb((struct sk_buff *) _skb); old_tail = tail; tail = (tail + 1) & (call->acks_winsz - 1); diff --git a/net/rxrpc/ar-connevent.c b/net/rxrpc/ar-connevent.c index 0505cdc4d6d..e7ed43a54c4 100644 --- a/net/rxrpc/ar-connevent.c +++ b/net/rxrpc/ar-connevent.c @@ -259,7 +259,6 @@ void rxrpc_process_connection(struct work_struct *work) { struct rxrpc_connection *conn = container_of(work, struct rxrpc_connection, processor); - struct rxrpc_skb_priv *sp; struct sk_buff *skb; u32 abort_code = RX_PROTOCOL_ERROR; int ret; @@ -276,8 +275,6 @@ void rxrpc_process_connection(struct work_struct *work) /* go through the conn-level event packets, releasing the ref on this * connection that each one has when we've finished with it */ while ((skb = skb_dequeue(&conn->rx_queue))) { - sp = rxrpc_skb(skb); - ret = rxrpc_process_event(conn, skb, &abort_code); switch (ret) { case -EPROTO: diff --git a/net/rxrpc/ar-error.c b/net/rxrpc/ar-error.c index d4d1ae26d29..5d6b572a670 100644 --- a/net/rxrpc/ar-error.c +++ b/net/rxrpc/ar-error.c @@ -139,7 +139,7 @@ void rxrpc_UDP_error_handler(struct work_struct *work) struct rxrpc_transport *trans = container_of(work, struct rxrpc_transport, error_handler); struct sk_buff *skb; - int local, err; + int err; _enter(""); @@ -157,7 +157,6 @@ void rxrpc_UDP_error_handler(struct work_struct *work) switch (ee->ee_origin) { case SO_EE_ORIGIN_ICMP: - local = 0; switch (ee->ee_type) { case ICMP_DEST_UNREACH: switch (ee->ee_code) { @@ -207,7 +206,6 @@ void rxrpc_UDP_error_handler(struct work_struct *work) case SO_EE_ORIGIN_LOCAL: _proto("Rx Received local error { error=%d }", ee->ee_errno); - local = 1; break; case SO_EE_ORIGIN_NONE: @@ -215,7 +213,6 @@ void rxrpc_UDP_error_handler(struct work_struct *work) default: _proto("Rx Received error report { orig=%u }", ee->ee_origin); - local = 0; break; } diff --git a/net/rxrpc/ar-peer.c b/net/rxrpc/ar-peer.c index 55b93dc60d0..2754f098d43 100644 --- a/net/rxrpc/ar-peer.c +++ b/net/rxrpc/ar-peer.c @@ -36,10 +36,11 @@ static void rxrpc_destroy_peer(struct work_struct *work); static void rxrpc_assess_MTU_size(struct rxrpc_peer *peer) { struct rtable *rt; + struct flowi4 fl4; peer->if_mtu = 1500; - rt = ip_route_output_ports(&init_net, NULL, + rt = ip_route_output_ports(&init_net, &fl4, NULL, peer->srx.transport.sin.sin_addr.s_addr, 0, htons(7000), htons(7001), IPPROTO_UDP, 0, 0); @@ -156,6 +157,7 @@ struct rxrpc_peer *rxrpc_get_peer(struct sockaddr_rxrpc *srx, gfp_t gfp) /* we can now add the new candidate to the list */ peer = candidate; candidate = NULL; + usage = atomic_read(&peer->usage); list_add_tail(&peer->link, &rxrpc_peers); write_unlock_bh(&rxrpc_peer_lock); @@ -170,7 +172,7 @@ success: &peer->srx.transport.sin.sin_addr, ntohs(peer->srx.transport.sin.sin_port)); - _leave(" = %p {u=%d}", peer, atomic_read(&peer->usage)); + _leave(" = %p {u=%d}", peer, usage); return peer; /* we found the peer in the list immediately */ diff --git a/net/rxrpc/ar-transport.c b/net/rxrpc/ar-transport.c index 5e0226fe587..92df566930b 100644 --- a/net/rxrpc/ar-transport.c +++ b/net/rxrpc/ar-transport.c @@ -111,6 +111,7 @@ struct rxrpc_transport *rxrpc_get_transport(struct rxrpc_local *local, /* we can now add the new candidate to the list */ trans = candidate; candidate = NULL; + usage = atomic_read(&trans->usage); rxrpc_get_local(trans->local); atomic_inc(&trans->peer->usage); @@ -125,7 +126,7 @@ success: trans->local->debug_id, trans->peer->debug_id); - _leave(" = %p {u=%d}", trans, atomic_read(&trans->usage)); + _leave(" = %p {u=%d}", trans, usage); return trans; /* we found the transport in the list immediately */ diff --git a/net/sched/Kconfig b/net/sched/Kconfig index aeaa2110b69..2590e91b328 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig @@ -288,6 +288,7 @@ config NET_CLS_TCINDEX config NET_CLS_ROUTE4 tristate "Routing decision (ROUTE)" + depends on INET select IP_ROUTE_CLASSID select NET_CLS ---help--- diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 14b42f4ad79..2f64262ab5d 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -26,11 +26,6 @@ #include <net/act_api.h> #include <net/netlink.h> -static void tcf_common_free_rcu(struct rcu_head *head) -{ - kfree(container_of(head, struct tcf_common, tcfc_rcu)); -} - void tcf_hash_destroy(struct tcf_common *p, struct tcf_hashinfo *hinfo) { unsigned int h = tcf_hash(p->tcfc_index, hinfo->hmask); @@ -47,7 +42,7 @@ void tcf_hash_destroy(struct tcf_common *p, struct tcf_hashinfo *hinfo) * gen_estimator est_timer() might access p->tcfc_lock * or bstats, wait a RCU grace period before freeing p */ - call_rcu(&p->tcfc_rcu, tcf_common_free_rcu); + kfree_rcu(p, tcfc_rcu); return; } } @@ -1120,9 +1115,10 @@ nlmsg_failure: static int __init tc_action_init(void) { - rtnl_register(PF_UNSPEC, RTM_NEWACTION, tc_ctl_action, NULL); - rtnl_register(PF_UNSPEC, RTM_DELACTION, tc_ctl_action, NULL); - rtnl_register(PF_UNSPEC, RTM_GETACTION, tc_ctl_action, tc_dump_action); + rtnl_register(PF_UNSPEC, RTM_NEWACTION, tc_ctl_action, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_DELACTION, tc_ctl_action, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_GETACTION, tc_ctl_action, tc_dump_action, + NULL); return 0; } diff --git a/net/sched/act_police.c b/net/sched/act_police.c index 8a1630774fd..b3b9b32f4e0 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -96,11 +96,6 @@ nla_put_failure: goto done; } -static void tcf_police_free_rcu(struct rcu_head *head) -{ - kfree(container_of(head, struct tcf_police, tcf_rcu)); -} - static void tcf_police_destroy(struct tcf_police *p) { unsigned int h = tcf_hash(p->tcf_index, POL_TAB_MASK); @@ -121,7 +116,7 @@ static void tcf_police_destroy(struct tcf_police *p) * gen_estimator est_timer() might access p->tcf_lock * or bstats, wait a RCU grace period before freeing p */ - call_rcu(&p->tcf_rcu, tcf_police_free_rcu); + kfree_rcu(p, tcf_rcu); return; } } @@ -401,7 +396,6 @@ static void __exit police_cleanup_module(void) { tcf_unregister_action(&act_police_ops); - rcu_barrier(); /* Wait for completion of call_rcu()'s (tcf_police_free_rcu) */ } module_init(police_init_module); diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index bb2c523f815..9563887f219 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -610,10 +610,10 @@ EXPORT_SYMBOL(tcf_exts_dump_stats); static int __init tc_filter_init(void) { - rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_ctl_tfilter, NULL); - rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_ctl_tfilter, NULL); + rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_ctl_tfilter, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_ctl_tfilter, NULL, NULL); rtnl_register(PF_UNSPEC, RTM_GETTFILTER, tc_ctl_tfilter, - tc_dump_tfilter); + tc_dump_tfilter, NULL); return 0; } diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 7490f3f2db8..8182aefafb0 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -1673,10 +1673,8 @@ int tc_classify(struct sk_buff *skb, struct tcf_proto *tp, { int err = 0; #ifdef CONFIG_NET_CLS_ACT - __be16 protocol; struct tcf_proto *otp = tp; reclassify: - protocol = skb->protocol; #endif err = tc_classify_compat(skb, tp, res); @@ -1794,12 +1792,12 @@ static int __init pktsched_init(void) register_qdisc(&pfifo_head_drop_qdisc_ops); register_qdisc(&mq_qdisc_ops); - rtnl_register(PF_UNSPEC, RTM_NEWQDISC, tc_modify_qdisc, NULL); - rtnl_register(PF_UNSPEC, RTM_DELQDISC, tc_get_qdisc, NULL); - rtnl_register(PF_UNSPEC, RTM_GETQDISC, tc_get_qdisc, tc_dump_qdisc); - rtnl_register(PF_UNSPEC, RTM_NEWTCLASS, tc_ctl_tclass, NULL); - rtnl_register(PF_UNSPEC, RTM_DELTCLASS, tc_ctl_tclass, NULL); - rtnl_register(PF_UNSPEC, RTM_GETTCLASS, tc_ctl_tclass, tc_dump_tclass); + rtnl_register(PF_UNSPEC, RTM_NEWQDISC, tc_modify_qdisc, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_DELQDISC, tc_get_qdisc, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_GETQDISC, tc_get_qdisc, tc_dump_qdisc, NULL); + rtnl_register(PF_UNSPEC, RTM_NEWTCLASS, tc_ctl_tclass, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_DELTCLASS, tc_ctl_tclass, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_GETTCLASS, tc_ctl_tclass, tc_dump_tclass, NULL); return 0; } diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c index 3f08158b868..e25e49061a0 100644 --- a/net/sched/sch_atm.c +++ b/net/sched/sch_atm.c @@ -5,6 +5,7 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/init.h> +#include <linux/interrupt.h> #include <linux/string.h> #include <linux/errno.h> #include <linux/skbuff.h> diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index c84b65920d1..b1721d71c27 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -815,9 +815,17 @@ static bool some_qdisc_is_busy(struct net_device *dev) return false; } +/** + * dev_deactivate_many - deactivate transmissions on several devices + * @head: list of devices to deactivate + * + * This function returns only when all outstanding transmissions + * have completed, unless all devices are in dismantle phase. + */ void dev_deactivate_many(struct list_head *head) { struct net_device *dev; + bool sync_needed = false; list_for_each_entry(dev, head, unreg_list) { netdev_for_each_tx_queue(dev, dev_deactivate_queue, @@ -827,10 +835,15 @@ void dev_deactivate_many(struct list_head *head) &noop_qdisc); dev_watchdog_down(dev); + sync_needed |= !dev->dismantle; } - /* Wait for outstanding qdisc-less dev_queue_xmit calls. */ - synchronize_rcu(); + /* Wait for outstanding qdisc-less dev_queue_xmit calls. + * This is avoided if all devices are in dismantle phase : + * Caller will call synchronize_net() for us + */ + if (sync_needed) + synchronize_net(); /* Wait for outstanding qdisc_run calls. */ list_for_each_entry(dev, head, unreg_list) diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index c2e628dfaac..b6ea6afa55b 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -169,7 +169,7 @@ static unsigned int sfq_hash(struct sfq_sched_data *q, struct sk_buff *skb) } case htons(ETH_P_IPV6): { - struct ipv6hdr *iph; + const struct ipv6hdr *iph; int poff; if (!pskb_network_may_pull(skb, sizeof(*iph))) @@ -361,7 +361,7 @@ sfq_enqueue(struct sk_buff *skb, struct Qdisc *sch) { struct sfq_sched_data *q = qdisc_priv(sch); unsigned int hash; - sfq_index x; + sfq_index x, qlen; struct sfq_slot *slot; int uninitialized_var(ret); @@ -405,20 +405,12 @@ sfq_enqueue(struct sk_buff *skb, struct Qdisc *sch) if (++sch->q.qlen <= q->limit) return NET_XMIT_SUCCESS; + qlen = slot->qlen; sfq_drop(sch); - return NET_XMIT_CN; -} - -static struct sk_buff * -sfq_peek(struct Qdisc *sch) -{ - struct sfq_sched_data *q = qdisc_priv(sch); - - /* No active slots */ - if (q->tail == NULL) - return NULL; - - return q->slots[q->tail->next].skblist_next; + /* Return Congestion Notification only if we dropped a packet + * from this flow. + */ + return (qlen != slot->qlen) ? NET_XMIT_CN : NET_XMIT_SUCCESS; } static struct sk_buff * @@ -702,7 +694,7 @@ static struct Qdisc_ops sfq_qdisc_ops __read_mostly = { .priv_size = sizeof(struct sfq_sched_data), .enqueue = sfq_enqueue, .dequeue = sfq_dequeue, - .peek = sfq_peek, + .peek = qdisc_peek_dequeued, .drop = sfq_drop, .init = sfq_init, .reset = sfq_reset, diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 1a21c571aa0..dc16b90ddb6 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -64,6 +64,7 @@ /* Forward declarations for internal functions. */ static void sctp_assoc_bh_rcv(struct work_struct *work); static void sctp_assoc_free_asconf_acks(struct sctp_association *asoc); +static void sctp_assoc_free_asconf_queue(struct sctp_association *asoc); /* Keep track of the new idr low so that we don't re-use association id * numbers too fast. It is protected by they idr spin lock is in the @@ -279,6 +280,8 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a asoc->peer.asconf_capable = 0; if (sctp_addip_noauth) asoc->peer.asconf_capable = 1; + asoc->asconf_addr_del_pending = NULL; + asoc->src_out_of_asoc_ok = 0; /* Create an input queue. */ sctp_inq_init(&asoc->base.inqueue); @@ -443,12 +446,11 @@ void sctp_association_free(struct sctp_association *asoc) asoc->peer.transport_count = 0; - /* Free any cached ASCONF_ACK chunk. */ - sctp_assoc_free_asconf_acks(asoc); + sctp_asconf_queue_teardown(asoc); - /* Free any cached ASCONF chunk. */ - if (asoc->addip_last_asconf) - sctp_chunk_free(asoc->addip_last_asconf); + /* Free pending address space being deleted */ + if (asoc->asconf_addr_del_pending != NULL) + kfree(asoc->asconf_addr_del_pending); /* AUTH - Free the endpoint shared keys */ sctp_auth_destroy_keys(&asoc->endpoint_shared_keys); @@ -1578,6 +1580,18 @@ retry: return error; } +/* Free the ASCONF queue */ +static void sctp_assoc_free_asconf_queue(struct sctp_association *asoc) +{ + struct sctp_chunk *asconf; + struct sctp_chunk *tmp; + + list_for_each_entry_safe(asconf, tmp, &asoc->addip_chunk_list, list) { + list_del_init(&asconf->list); + sctp_chunk_free(asconf); + } +} + /* Free asconf_ack cache */ static void sctp_assoc_free_asconf_acks(struct sctp_association *asoc) { @@ -1630,3 +1644,16 @@ struct sctp_chunk *sctp_assoc_lookup_asconf_ack( return NULL; } + +void sctp_asconf_queue_teardown(struct sctp_association *asoc) +{ + /* Free any cached ASCONF_ACK chunk. */ + sctp_assoc_free_asconf_acks(asoc); + + /* Free the ASCONF queue. */ + sctp_assoc_free_asconf_queue(asoc); + + /* Free any cached ASCONF chunk. */ + if (asoc->addip_last_asconf) + sctp_chunk_free(asoc->addip_last_asconf); +} diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c index faf71d179e4..17d157325b6 100644 --- a/net/sctp/bind_addr.c +++ b/net/sctp/bind_addr.c @@ -140,14 +140,12 @@ void sctp_bind_addr_init(struct sctp_bind_addr *bp, __u16 port) /* Dispose of the address list. */ static void sctp_bind_addr_clean(struct sctp_bind_addr *bp) { - struct sctp_sockaddr_entry *addr; - struct list_head *pos, *temp; + struct sctp_sockaddr_entry *addr, *temp; /* Empty the bind address list. */ - list_for_each_safe(pos, temp, &bp->address_list) { - addr = list_entry(pos, struct sctp_sockaddr_entry, list); - list_del(pos); - kfree(addr); + list_for_each_entry_safe(addr, temp, &bp->address_list, list) { + list_del_rcu(&addr->list); + kfree_rcu(addr, rcu); SCTP_DBG_OBJCNT_DEC(addr); } } @@ -219,7 +217,7 @@ int sctp_del_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *del_addr) } if (found) { - call_rcu(&addr->rcu, sctp_local_addr_free); + kfree_rcu(addr, rcu); SCTP_DBG_OBJCNT_DEC(addr); return 0; } @@ -536,6 +534,21 @@ int sctp_in_scope(const union sctp_addr *addr, sctp_scope_t scope) return 0; } +int sctp_is_ep_boundall(struct sock *sk) +{ + struct sctp_bind_addr *bp; + struct sctp_sockaddr_entry *addr; + + bp = &sctp_sk(sk)->ep->base.bind_addr; + if (sctp_list_single_entry(&bp->address_list)) { + addr = list_entry(bp->address_list.next, + struct sctp_sockaddr_entry, list); + if (sctp_is_any(sk, &addr->a)) + return 1; + } + return 0; +} + /******************************************************************** * 3rd Level Abstractions ********************************************************************/ diff --git a/net/sctp/debug.c b/net/sctp/debug.c index bf24fa697de..ec997cfe0a7 100644 --- a/net/sctp/debug.c +++ b/net/sctp/debug.c @@ -98,7 +98,6 @@ const char *sctp_cname(const sctp_subtype_t cid) /* These are printable forms of the states. */ const char *const sctp_state_tbl[SCTP_STATE_NUM_STATES] = { - "STATE_EMPTY", "STATE_CLOSED", "STATE_COOKIE_WAIT", "STATE_COOKIE_ECHOED", diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c index e10acc01c75..c8cc24e282c 100644 --- a/net/sctp/endpointola.c +++ b/net/sctp/endpointola.c @@ -325,6 +325,7 @@ static struct sctp_association *__sctp_endpoint_lookup_assoc( struct sctp_transport **transport) { struct sctp_association *asoc = NULL; + struct sctp_association *tmp; struct sctp_transport *t = NULL; struct sctp_hashbucket *head; struct sctp_ep_common *epb; @@ -333,25 +334,32 @@ static struct sctp_association *__sctp_endpoint_lookup_assoc( int rport; *transport = NULL; + + /* If the local port is not set, there can't be any associations + * on this endpoint. + */ + if (!ep->base.bind_addr.port) + goto out; + rport = ntohs(paddr->v4.sin_port); hash = sctp_assoc_hashfn(ep->base.bind_addr.port, rport); head = &sctp_assoc_hashtable[hash]; read_lock(&head->lock); sctp_for_each_hentry(epb, node, &head->chain) { - asoc = sctp_assoc(epb); - if (asoc->ep != ep || rport != asoc->peer.port) - goto next; + tmp = sctp_assoc(epb); + if (tmp->ep != ep || rport != tmp->peer.port) + continue; - t = sctp_assoc_lookup_paddr(asoc, paddr); + t = sctp_assoc_lookup_paddr(tmp, paddr); if (t) { + asoc = tmp; *transport = t; break; } -next: - asoc = NULL; } read_unlock(&head->lock); +out: return asoc; } diff --git a/net/sctp/input.c b/net/sctp/input.c index 30cec7732e8..741ed164883 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -565,7 +565,7 @@ void sctp_err_finish(struct sock *sk, struct sctp_association *asoc) */ void sctp_v4_err(struct sk_buff *skb, __u32 info) { - struct iphdr *iph = (struct iphdr *)skb->data; + const struct iphdr *iph = (const struct iphdr *)skb->data; const int ihlen = iph->ihl * 4; const int type = icmp_hdr(skb)->type; const int code = icmp_hdr(skb)->code; @@ -661,7 +661,6 @@ static int sctp_rcv_ootb(struct sk_buff *skb) { sctp_chunkhdr_t *ch; __u8 *ch_end; - sctp_errhdr_t *err; ch = (sctp_chunkhdr_t *) skb->data; @@ -697,20 +696,6 @@ static int sctp_rcv_ootb(struct sk_buff *skb) if (SCTP_CID_INIT == ch->type && (void *)ch != skb->data) goto discard; - /* RFC 8.4, 7) If the packet contains a "Stale cookie" ERROR - * or a COOKIE ACK the SCTP Packet should be silently - * discarded. - */ - if (SCTP_CID_COOKIE_ACK == ch->type) - goto discard; - - if (SCTP_CID_ERROR == ch->type) { - sctp_walk_errors(err, ch) { - if (SCTP_ERROR_STALE_COOKIE == err->cause) - goto discard; - } - } - ch = (sctp_chunkhdr_t *) ch_end; } while (ch_end < skb_tail_pointer(skb)); diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 865ce7ba4e1..aabaee41dd3 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -80,6 +80,13 @@ #include <asm/uaccess.h> +static inline int sctp_v6_addr_match_len(union sctp_addr *s1, + union sctp_addr *s2); +static void sctp_v6_to_addr(union sctp_addr *addr, struct in6_addr *saddr, + __be16 port); +static int sctp_v6_cmp_addr(const union sctp_addr *addr1, + const union sctp_addr *addr2); + /* Event handler for inet6 address addition/deletion events. * The sctp_local_addr_list needs to be protocted by a spin lock since * multiple notifiers (say IPv4 and IPv6) may be running at the same @@ -105,6 +112,7 @@ static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev, addr->valid = 1; spin_lock_bh(&sctp_local_addr_lock); list_add_tail_rcu(&addr->list, &sctp_local_addr_list); + sctp_addr_wq_mgmt(addr, SCTP_ADDR_NEW); spin_unlock_bh(&sctp_local_addr_lock); } break; @@ -115,6 +123,7 @@ static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev, if (addr->a.sa.sa_family == AF_INET6 && ipv6_addr_equal(&addr->a.v6.sin6_addr, &ifa->addr)) { + sctp_addr_wq_mgmt(addr, SCTP_ADDR_DEL); found = 1; addr->valid = 0; list_del_rcu(&addr->list); @@ -123,7 +132,7 @@ static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev, } spin_unlock_bh(&sctp_local_addr_lock); if (found) - call_rcu(&addr->rcu, sctp_local_addr_free); + kfree_rcu(addr, rcu); break; } @@ -240,37 +249,107 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport) /* Returns the dst cache entry for the given source and destination ip * addresses. */ -static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc, - union sctp_addr *daddr, - union sctp_addr *saddr) +static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr, + struct flowi *fl, struct sock *sk) { - struct dst_entry *dst; - struct flowi6 fl6; + struct sctp_association *asoc = t->asoc; + struct dst_entry *dst = NULL; + struct flowi6 *fl6 = &fl->u.ip6; + struct sctp_bind_addr *bp; + struct sctp_sockaddr_entry *laddr; + union sctp_addr *baddr = NULL; + union sctp_addr *daddr = &t->ipaddr; + union sctp_addr dst_saddr; + __u8 matchlen = 0; + __u8 bmatchlen; + sctp_scope_t scope; - memset(&fl6, 0, sizeof(fl6)); - ipv6_addr_copy(&fl6.daddr, &daddr->v6.sin6_addr); + memset(fl6, 0, sizeof(struct flowi6)); + ipv6_addr_copy(&fl6->daddr, &daddr->v6.sin6_addr); + fl6->fl6_dport = daddr->v6.sin6_port; + fl6->flowi6_proto = IPPROTO_SCTP; if (ipv6_addr_type(&daddr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) - fl6.flowi6_oif = daddr->v6.sin6_scope_id; + fl6->flowi6_oif = daddr->v6.sin6_scope_id; + SCTP_DEBUG_PRINTK("%s: DST=%pI6 ", __func__, &fl6->daddr); - SCTP_DEBUG_PRINTK("%s: DST=%pI6 ", __func__, &fl6.daddr); + if (asoc) + fl6->fl6_sport = htons(asoc->base.bind_addr.port); if (saddr) { - ipv6_addr_copy(&fl6.saddr, &saddr->v6.sin6_addr); - SCTP_DEBUG_PRINTK("SRC=%pI6 - ", &fl6.saddr); + ipv6_addr_copy(&fl6->saddr, &saddr->v6.sin6_addr); + fl6->fl6_sport = saddr->v6.sin6_port; + SCTP_DEBUG_PRINTK("SRC=%pI6 - ", &fl6->saddr); + } + + dst = ip6_dst_lookup_flow(sk, fl6, NULL, false); + if (!asoc || saddr) + goto out; + + bp = &asoc->base.bind_addr; + scope = sctp_scope(daddr); + /* ip6_dst_lookup has filled in the fl6->saddr for us. Check + * to see if we can use it. + */ + if (!IS_ERR(dst)) { + /* Walk through the bind address list and look for a bind + * address that matches the source address of the returned dst. + */ + sctp_v6_to_addr(&dst_saddr, &fl6->saddr, htons(bp->port)); + rcu_read_lock(); + list_for_each_entry_rcu(laddr, &bp->address_list, list) { + if (!laddr->valid || (laddr->state != SCTP_ADDR_SRC)) + continue; + + /* Do not compare against v4 addrs */ + if ((laddr->a.sa.sa_family == AF_INET6) && + (sctp_v6_cmp_addr(&dst_saddr, &laddr->a))) { + rcu_read_unlock(); + goto out; + } + } + rcu_read_unlock(); + /* None of the bound addresses match the source address of the + * dst. So release it. + */ + dst_release(dst); + dst = NULL; } - dst = ip6_route_output(&init_net, NULL, &fl6); - if (!dst->error) { + /* Walk through the bind address list and try to get the + * best source address for a given destination. + */ + rcu_read_lock(); + list_for_each_entry_rcu(laddr, &bp->address_list, list) { + if (!laddr->valid && laddr->state != SCTP_ADDR_SRC) + continue; + if ((laddr->a.sa.sa_family == AF_INET6) && + (scope <= sctp_scope(&laddr->a))) { + bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a); + if (!baddr || (matchlen < bmatchlen)) { + baddr = &laddr->a; + matchlen = bmatchlen; + } + } + } + rcu_read_unlock(); + if (baddr) { + ipv6_addr_copy(&fl6->saddr, &baddr->v6.sin6_addr); + fl6->fl6_sport = baddr->v6.sin6_port; + dst = ip6_dst_lookup_flow(sk, fl6, NULL, false); + } + +out: + if (!IS_ERR(dst)) { struct rt6_info *rt; rt = (struct rt6_info *)dst; + t->dst = dst; SCTP_DEBUG_PRINTK("rt6_dst:%pI6 rt6_src:%pI6\n", - &rt->rt6i_dst.addr, &rt->rt6i_src.addr); - return dst; + &rt->rt6i_dst.addr, &fl6->saddr); + } else { + t->dst = NULL; + SCTP_DEBUG_PRINTK("NO ROUTE\n"); } - SCTP_DEBUG_PRINTK("NO ROUTE\n"); - dst_release(dst); - return NULL; } /* Returns the number of consecutive initial bits that match in the 2 ipv6 @@ -286,64 +365,18 @@ static inline int sctp_v6_addr_match_len(union sctp_addr *s1, * and asoc's bind address list. */ static void sctp_v6_get_saddr(struct sctp_sock *sk, - struct sctp_association *asoc, - struct dst_entry *dst, - union sctp_addr *daddr, - union sctp_addr *saddr) + struct sctp_transport *t, + struct flowi *fl) { - struct sctp_bind_addr *bp; - struct sctp_sockaddr_entry *laddr; - sctp_scope_t scope; - union sctp_addr *baddr = NULL; - __u8 matchlen = 0; - __u8 bmatchlen; + struct flowi6 *fl6 = &fl->u.ip6; + union sctp_addr *saddr = &t->saddr; - SCTP_DEBUG_PRINTK("%s: asoc:%p dst:%p daddr:%pI6 ", - __func__, asoc, dst, &daddr->v6.sin6_addr); - - if (!asoc) { - ipv6_dev_get_saddr(sock_net(sctp_opt2sk(sk)), - dst ? ip6_dst_idev(dst)->dev : NULL, - &daddr->v6.sin6_addr, - inet6_sk(&sk->inet.sk)->srcprefs, - &saddr->v6.sin6_addr); - SCTP_DEBUG_PRINTK("saddr from ipv6_get_saddr: %pI6\n", - &saddr->v6.sin6_addr); - return; - } - - scope = sctp_scope(daddr); - - bp = &asoc->base.bind_addr; + SCTP_DEBUG_PRINTK("%s: asoc:%p dst:%p\n", __func__, t->asoc, t->dst); - /* Go through the bind address list and find the best source address - * that matches the scope of the destination address. - */ - rcu_read_lock(); - list_for_each_entry_rcu(laddr, &bp->address_list, list) { - if (!laddr->valid) - continue; - if ((laddr->state == SCTP_ADDR_SRC) && - (laddr->a.sa.sa_family == AF_INET6) && - (scope <= sctp_scope(&laddr->a))) { - bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a); - if (!baddr || (matchlen < bmatchlen)) { - baddr = &laddr->a; - matchlen = bmatchlen; - } - } - } - - if (baddr) { - memcpy(saddr, baddr, sizeof(union sctp_addr)); - SCTP_DEBUG_PRINTK("saddr: %pI6\n", &saddr->v6.sin6_addr); - } else { - pr_err("%s: asoc:%p Could not find a valid source " - "address for the dest:%pI6\n", - __func__, asoc, &daddr->v6.sin6_addr); + if (t->dst) { + saddr->v6.sin6_family = AF_INET6; + ipv6_addr_copy(&saddr->v6.sin6_addr, &fl6->saddr); } - - rcu_read_unlock(); } /* Make a copy of all potential local addresses. */ @@ -465,14 +498,13 @@ static int sctp_v6_to_addr_param(const union sctp_addr *addr, return length; } -/* Initialize a sctp_addr from a dst_entry. */ -static void sctp_v6_dst_saddr(union sctp_addr *addr, struct dst_entry *dst, +/* Initialize a sctp_addr from struct in6_addr. */ +static void sctp_v6_to_addr(union sctp_addr *addr, struct in6_addr *saddr, __be16 port) { - struct rt6_info *rt = (struct rt6_info *)dst; addr->sa.sa_family = AF_INET6; addr->v6.sin6_port = port; - ipv6_addr_copy(&addr->v6.sin6_addr, &rt->rt6i_src.addr); + ipv6_addr_copy(&addr->v6.sin6_addr, saddr); } /* Compare addresses exactly. @@ -531,7 +563,7 @@ static int sctp_v6_is_any(const union sctp_addr *addr) static int sctp_v6_available(union sctp_addr *addr, struct sctp_sock *sp) { int type; - struct in6_addr *in6 = (struct in6_addr *)&addr->v6.sin6_addr; + const struct in6_addr *in6 = (const struct in6_addr *)&addr->v6.sin6_addr; type = ipv6_addr_type(in6); if (IPV6_ADDR_ANY == type) @@ -959,7 +991,6 @@ static struct sctp_af sctp_af_inet6 = { .to_sk_daddr = sctp_v6_to_sk_daddr, .from_addr_param = sctp_v6_from_addr_param, .to_addr_param = sctp_v6_to_addr_param, - .dst_saddr = sctp_v6_dst_saddr, .cmp_addr = sctp_v6_cmp_addr, .scope = sctp_v6_scope, .addr_valid = sctp_v6_addr_valid, diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index 7812772dbf7..edc753297a4 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -320,7 +320,6 @@ int sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk) * chunk. */ switch (q->asoc->state) { - case SCTP_STATE_EMPTY: case SCTP_STATE_CLOSED: case SCTP_STATE_SHUTDOWN_PENDING: case SCTP_STATE_SHUTDOWN_SENT: @@ -578,6 +577,13 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, * try to send as much as possible. */ list_for_each_entry_safe(chunk, chunk1, lqueue, transmitted_list) { + /* If the chunk is abandoned, move it to abandoned list. */ + if (sctp_chunk_abandoned(chunk)) { + list_del_init(&chunk->transmitted_list); + sctp_insert_list(&q->abandoned, + &chunk->transmitted_list); + continue; + } /* Make sure that Gap Acked TSNs are not retransmitted. A * simple approach is just to move such TSNs out of the @@ -748,6 +754,16 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) */ list_for_each_entry_safe(chunk, tmp, &q->control_chunk_list, list) { + /* RFC 5061, 5.3 + * F1) This means that until such time as the ASCONF + * containing the add is acknowledged, the sender MUST + * NOT use the new IP address as a source for ANY SCTP + * packet except on carrying an ASCONF Chunk. + */ + if (asoc->src_out_of_asoc_ok && + chunk->chunk_hdr->type != SCTP_CID_ASCONF) + continue; + list_del_init(&chunk->list); /* Pick the right transport to use. */ @@ -875,6 +891,9 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) } } + if (q->asoc->src_out_of_asoc_ok) + goto sctp_flush_out; + /* Is it OK to send data chunks? */ switch (asoc->state) { case SCTP_STATE_COOKIE_ECHOED: diff --git a/net/sctp/proc.c b/net/sctp/proc.c index 61aacfbbaa9..05a6ce21471 100644 --- a/net/sctp/proc.c +++ b/net/sctp/proc.c @@ -212,7 +212,7 @@ static int sctp_eps_seq_show(struct seq_file *seq, void *v) sctp_for_each_hentry(epb, node, &head->chain) { ep = sctp_ep(epb); sk = epb->sk; - seq_printf(seq, "%8p %8p %-3d %-3d %-4d %-5d %5d %5lu ", ep, sk, + seq_printf(seq, "%8pK %8pK %-3d %-3d %-4d %-5d %5d %5lu ", ep, sk, sctp_sk(sk)->type, sk->sk_state, hash, epb->bind_addr.port, sock_i_uid(sk), sock_i_ino(sk)); @@ -316,7 +316,7 @@ static int sctp_assocs_seq_show(struct seq_file *seq, void *v) assoc = sctp_assoc(epb); sk = epb->sk; seq_printf(seq, - "%8p %8p %-3d %-3d %-2d %-4d " + "%8pK %8pK %-3d %-3d %-2d %-4d " "%4d %8d %8d %7d %5lu %-5d %5d ", assoc, sk, sctp_sk(sk)->type, sk->sk_state, assoc->state, hash, diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index d5bf91d04f6..ab5ded2c58d 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -230,13 +230,6 @@ static void sctp_free_local_addr_list(void) } } -void sctp_local_addr_free(struct rcu_head *head) -{ - struct sctp_sockaddr_entry *e = container_of(head, - struct sctp_sockaddr_entry, rcu); - kfree(e); -} - /* Copy the local addresses which are valid for 'scope' into 'bp'. */ int sctp_copy_local_addr_list(struct sctp_bind_addr *bp, sctp_scope_t scope, gfp_t gfp, int copy_flags) @@ -339,13 +332,12 @@ static int sctp_v4_to_addr_param(const union sctp_addr *addr, } /* Initialize a sctp_addr from a dst_entry. */ -static void sctp_v4_dst_saddr(union sctp_addr *saddr, struct dst_entry *dst, +static void sctp_v4_dst_saddr(union sctp_addr *saddr, struct flowi4 *fl4, __be16 port) { - struct rtable *rt = (struct rtable *)dst; saddr->v4.sin_family = AF_INET; saddr->v4.sin_port = port; - saddr->v4.sin_addr.s_addr = rt->rt_src; + saddr->v4.sin_addr.s_addr = fl4->saddr; } /* Compare two addresses exactly. */ @@ -463,35 +455,36 @@ static sctp_scope_t sctp_v4_scope(union sctp_addr *addr) * addresses. If an association is passed, trys to get a dst entry with a * source address that matches an address in the bind address list. */ -static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc, - union sctp_addr *daddr, - union sctp_addr *saddr) +static void sctp_v4_get_dst(struct sctp_transport *t, union sctp_addr *saddr, + struct flowi *fl, struct sock *sk) { + struct sctp_association *asoc = t->asoc; struct rtable *rt; - struct flowi4 fl4; + struct flowi4 *fl4 = &fl->u.ip4; struct sctp_bind_addr *bp; struct sctp_sockaddr_entry *laddr; struct dst_entry *dst = NULL; + union sctp_addr *daddr = &t->ipaddr; union sctp_addr dst_saddr; - memset(&fl4, 0x0, sizeof(struct flowi4)); - fl4.daddr = daddr->v4.sin_addr.s_addr; - fl4.fl4_dport = daddr->v4.sin_port; - fl4.flowi4_proto = IPPROTO_SCTP; + memset(fl4, 0x0, sizeof(struct flowi4)); + fl4->daddr = daddr->v4.sin_addr.s_addr; + fl4->fl4_dport = daddr->v4.sin_port; + fl4->flowi4_proto = IPPROTO_SCTP; if (asoc) { - fl4.flowi4_tos = RT_CONN_FLAGS(asoc->base.sk); - fl4.flowi4_oif = asoc->base.sk->sk_bound_dev_if; - fl4.fl4_sport = htons(asoc->base.bind_addr.port); + fl4->flowi4_tos = RT_CONN_FLAGS(asoc->base.sk); + fl4->flowi4_oif = asoc->base.sk->sk_bound_dev_if; + fl4->fl4_sport = htons(asoc->base.bind_addr.port); } if (saddr) { - fl4.saddr = saddr->v4.sin_addr.s_addr; - fl4.fl4_sport = saddr->v4.sin_port; + fl4->saddr = saddr->v4.sin_addr.s_addr; + fl4->fl4_sport = saddr->v4.sin_port; } SCTP_DEBUG_PRINTK("%s: DST:%pI4, SRC:%pI4 - ", - __func__, &fl4.daddr, &fl4.saddr); + __func__, &fl4->daddr, &fl4->saddr); - rt = ip_route_output_key(&init_net, &fl4); + rt = ip_route_output_key(&init_net, fl4); if (!IS_ERR(rt)) dst = &rt->dst; @@ -507,10 +500,12 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc, /* Walk through the bind address list and look for a bind * address that matches the source address of the returned dst. */ - sctp_v4_dst_saddr(&dst_saddr, dst, htons(bp->port)); + sctp_v4_dst_saddr(&dst_saddr, fl4, htons(bp->port)); rcu_read_lock(); list_for_each_entry_rcu(laddr, &bp->address_list, list) { - if (!laddr->valid || (laddr->state != SCTP_ADDR_SRC)) + if (!laddr->valid || (laddr->state == SCTP_ADDR_DEL) || + (laddr->state != SCTP_ADDR_SRC && + !asoc->src_out_of_asoc_ok)) continue; if (sctp_v4_cmp_addr(&dst_saddr, &laddr->a)) goto out_unlock; @@ -533,9 +528,9 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc, continue; if ((laddr->state == SCTP_ADDR_SRC) && (AF_INET == laddr->a.sa.sa_family)) { - fl4.saddr = laddr->a.v4.sin_addr.s_addr; - fl4.fl4_sport = laddr->a.v4.sin_port; - rt = ip_route_output_key(&init_net, &fl4); + fl4->saddr = laddr->a.v4.sin_addr.s_addr; + fl4->fl4_sport = laddr->a.v4.sin_port; + rt = ip_route_output_key(&init_net, fl4); if (!IS_ERR(rt)) { dst = &rt->dst; goto out_unlock; @@ -546,33 +541,27 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc, out_unlock: rcu_read_unlock(); out: + t->dst = dst; if (dst) SCTP_DEBUG_PRINTK("rt_dst:%pI4, rt_src:%pI4\n", - &rt->rt_dst, &rt->rt_src); + &fl4->daddr, &fl4->saddr); else SCTP_DEBUG_PRINTK("NO ROUTE\n"); - - return dst; } /* For v4, the source address is cached in the route entry(dst). So no need * to cache it separately and hence this is an empty routine. */ static void sctp_v4_get_saddr(struct sctp_sock *sk, - struct sctp_association *asoc, - struct dst_entry *dst, - union sctp_addr *daddr, - union sctp_addr *saddr) + struct sctp_transport *t, + struct flowi *fl) { - struct rtable *rt = (struct rtable *)dst; - - if (!asoc) - return; + union sctp_addr *saddr = &t->saddr; + struct rtable *rt = (struct rtable *)t->dst; if (rt) { saddr->v4.sin_family = AF_INET; - saddr->v4.sin_port = htons(asoc->base.bind_addr.port); - saddr->v4.sin_addr.s_addr = rt->rt_src; + saddr->v4.sin_addr.s_addr = fl->u.ip4.saddr; } } @@ -636,6 +625,143 @@ static void sctp_v4_ecn_capable(struct sock *sk) INET_ECN_xmit(sk); } +void sctp_addr_wq_timeout_handler(unsigned long arg) +{ + struct sctp_sockaddr_entry *addrw, *temp; + struct sctp_sock *sp; + + spin_lock_bh(&sctp_addr_wq_lock); + + list_for_each_entry_safe(addrw, temp, &sctp_addr_waitq, list) { + SCTP_DEBUG_PRINTK_IPADDR("sctp_addrwq_timo_handler: the first ent in wq %p is ", + " for cmd %d at entry %p\n", &sctp_addr_waitq, &addrw->a, addrw->state, + addrw); + +#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) + /* Now we send an ASCONF for each association */ + /* Note. we currently don't handle link local IPv6 addressees */ + if (addrw->a.sa.sa_family == AF_INET6) { + struct in6_addr *in6; + + if (ipv6_addr_type(&addrw->a.v6.sin6_addr) & + IPV6_ADDR_LINKLOCAL) + goto free_next; + + in6 = (struct in6_addr *)&addrw->a.v6.sin6_addr; + if (ipv6_chk_addr(&init_net, in6, NULL, 0) == 0 && + addrw->state == SCTP_ADDR_NEW) { + unsigned long timeo_val; + + SCTP_DEBUG_PRINTK("sctp_timo_handler: this is on DAD, trying %d sec later\n", + SCTP_ADDRESS_TICK_DELAY); + timeo_val = jiffies; + timeo_val += msecs_to_jiffies(SCTP_ADDRESS_TICK_DELAY); + mod_timer(&sctp_addr_wq_timer, timeo_val); + break; + } + } +#endif + list_for_each_entry(sp, &sctp_auto_asconf_splist, auto_asconf_list) { + struct sock *sk; + + sk = sctp_opt2sk(sp); + /* ignore bound-specific endpoints */ + if (!sctp_is_ep_boundall(sk)) + continue; + sctp_bh_lock_sock(sk); + if (sctp_asconf_mgmt(sp, addrw) < 0) + SCTP_DEBUG_PRINTK("sctp_addrwq_timo_handler: sctp_asconf_mgmt failed\n"); + sctp_bh_unlock_sock(sk); + } +free_next: + list_del(&addrw->list); + kfree(addrw); + } + spin_unlock_bh(&sctp_addr_wq_lock); +} + +static void sctp_free_addr_wq(void) +{ + struct sctp_sockaddr_entry *addrw; + struct sctp_sockaddr_entry *temp; + + spin_lock_bh(&sctp_addr_wq_lock); + del_timer(&sctp_addr_wq_timer); + list_for_each_entry_safe(addrw, temp, &sctp_addr_waitq, list) { + list_del(&addrw->list); + kfree(addrw); + } + spin_unlock_bh(&sctp_addr_wq_lock); +} + +/* lookup the entry for the same address in the addr_waitq + * sctp_addr_wq MUST be locked + */ +static struct sctp_sockaddr_entry *sctp_addr_wq_lookup(struct sctp_sockaddr_entry *addr) +{ + struct sctp_sockaddr_entry *addrw; + + list_for_each_entry(addrw, &sctp_addr_waitq, list) { + if (addrw->a.sa.sa_family != addr->a.sa.sa_family) + continue; + if (addrw->a.sa.sa_family == AF_INET) { + if (addrw->a.v4.sin_addr.s_addr == + addr->a.v4.sin_addr.s_addr) + return addrw; + } else if (addrw->a.sa.sa_family == AF_INET6) { + if (ipv6_addr_equal(&addrw->a.v6.sin6_addr, + &addr->a.v6.sin6_addr)) + return addrw; + } + } + return NULL; +} + +void sctp_addr_wq_mgmt(struct sctp_sockaddr_entry *addr, int cmd) +{ + struct sctp_sockaddr_entry *addrw; + unsigned long timeo_val; + + /* first, we check if an opposite message already exist in the queue. + * If we found such message, it is removed. + * This operation is a bit stupid, but the DHCP client attaches the + * new address after a couple of addition and deletion of that address + */ + + spin_lock_bh(&sctp_addr_wq_lock); + /* Offsets existing events in addr_wq */ + addrw = sctp_addr_wq_lookup(addr); + if (addrw) { + if (addrw->state != cmd) { + SCTP_DEBUG_PRINTK_IPADDR("sctp_addr_wq_mgmt offsets existing entry for %d ", + " in wq %p\n", addrw->state, &addrw->a, + &sctp_addr_waitq); + list_del(&addrw->list); + kfree(addrw); + } + spin_unlock_bh(&sctp_addr_wq_lock); + return; + } + + /* OK, we have to add the new address to the wait queue */ + addrw = kmemdup(addr, sizeof(struct sctp_sockaddr_entry), GFP_ATOMIC); + if (addrw == NULL) { + spin_unlock_bh(&sctp_addr_wq_lock); + return; + } + addrw->state = cmd; + list_add_tail(&addrw->list, &sctp_addr_waitq); + SCTP_DEBUG_PRINTK_IPADDR("sctp_addr_wq_mgmt add new entry for cmd:%d ", + " in wq %p\n", addrw->state, &addrw->a, &sctp_addr_waitq); + + if (!timer_pending(&sctp_addr_wq_timer)) { + timeo_val = jiffies; + timeo_val += msecs_to_jiffies(SCTP_ADDRESS_TICK_DELAY); + mod_timer(&sctp_addr_wq_timer, timeo_val); + } + spin_unlock_bh(&sctp_addr_wq_lock); +} + /* Event handler for inet address addition/deletion events. * The sctp_local_addr_list needs to be protocted by a spin lock since * multiple notifiers (say IPv4 and IPv6) may be running at the same @@ -663,6 +789,7 @@ static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev, addr->valid = 1; spin_lock_bh(&sctp_local_addr_lock); list_add_tail_rcu(&addr->list, &sctp_local_addr_list); + sctp_addr_wq_mgmt(addr, SCTP_ADDR_NEW); spin_unlock_bh(&sctp_local_addr_lock); } break; @@ -673,6 +800,7 @@ static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev, if (addr->a.sa.sa_family == AF_INET && addr->a.v4.sin_addr.s_addr == ifa->ifa_local) { + sctp_addr_wq_mgmt(addr, SCTP_ADDR_DEL); found = 1; addr->valid = 0; list_del_rcu(&addr->list); @@ -681,7 +809,7 @@ static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev, } spin_unlock_bh(&sctp_local_addr_lock); if (found) - call_rcu(&addr->rcu, sctp_local_addr_free); + kfree_rcu(addr, rcu); break; } @@ -854,14 +982,14 @@ static inline int sctp_v4_xmit(struct sk_buff *skb, SCTP_DEBUG_PRINTK("%s: skb:%p, len:%d, src:%pI4, dst:%pI4\n", __func__, skb, skb->len, - &skb_rtable(skb)->rt_src, - &skb_rtable(skb)->rt_dst); + &transport->fl.u.ip4.saddr, + &transport->fl.u.ip4.daddr); inet->pmtudisc = transport->param_flags & SPP_PMTUD_ENABLE ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT; SCTP_INC_STATS(SCTP_MIB_OUTSCTPPACKS); - return ip_queue_xmit(skb); + return ip_queue_xmit(skb, &transport->fl); } static struct sctp_af sctp_af_inet; @@ -950,7 +1078,6 @@ static struct sctp_af sctp_af_inet = { .to_sk_daddr = sctp_v4_to_sk_daddr, .from_addr_param = sctp_v4_from_addr_param, .to_addr_param = sctp_v4_to_addr_param, - .dst_saddr = sctp_v4_dst_saddr, .cmp_addr = sctp_v4_cmp_addr, .addr_valid = sctp_v4_addr_valid, .inaddr_any = sctp_v4_inaddr_any, @@ -1256,6 +1383,7 @@ SCTP_STATIC __init int sctp_init(void) /* Disable ADDIP by default. */ sctp_addip_enable = 0; sctp_addip_noauth = 0; + sctp_default_auto_asconf = 0; /* Enable PR-SCTP by default. */ sctp_prsctp_enable = 1; @@ -1280,6 +1408,13 @@ SCTP_STATIC __init int sctp_init(void) spin_lock_init(&sctp_local_addr_lock); sctp_get_local_addr_list(); + /* Initialize the address event list */ + INIT_LIST_HEAD(&sctp_addr_waitq); + INIT_LIST_HEAD(&sctp_auto_asconf_splist); + spin_lock_init(&sctp_addr_wq_lock); + sctp_addr_wq_timer.expires = 0; + setup_timer(&sctp_addr_wq_timer, sctp_addr_wq_timeout_handler, 0); + status = sctp_v4_protosw_init(); if (status) @@ -1351,6 +1486,7 @@ SCTP_STATIC __exit void sctp_exit(void) /* Unregister with inet6/inet layers. */ sctp_v6_del_protocol(); sctp_v4_del_protocol(); + sctp_free_addr_wq(); /* Free the control endpoint. */ inet_ctl_sock_destroy(sctp_ctl_sock); diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index f87ccb11a52..3363d378825 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1075,20 +1075,28 @@ nodata: /* Make a HEARTBEAT chunk. */ struct sctp_chunk *sctp_make_heartbeat(const struct sctp_association *asoc, - const struct sctp_transport *transport, - const void *payload, const size_t paylen) + const struct sctp_transport *transport) { - struct sctp_chunk *retval = sctp_make_chunk(asoc, SCTP_CID_HEARTBEAT, - 0, paylen); + struct sctp_chunk *retval; + sctp_sender_hb_info_t hbinfo; + + retval = sctp_make_chunk(asoc, SCTP_CID_HEARTBEAT, 0, sizeof(hbinfo)); if (!retval) goto nodata; + hbinfo.param_hdr.type = SCTP_PARAM_HEARTBEAT_INFO; + hbinfo.param_hdr.length = htons(sizeof(sctp_sender_hb_info_t)); + hbinfo.daddr = transport->ipaddr; + hbinfo.sent_at = jiffies; + hbinfo.hb_nonce = transport->hb_nonce; + /* Cast away the 'const', as this is just telling the chunk * what transport it belongs to. */ retval->transport = (struct sctp_transport *) transport; - retval->subh.hbs_hdr = sctp_addto_chunk(retval, paylen, payload); + retval->subh.hbs_hdr = sctp_addto_chunk(retval, sizeof(hbinfo), + &hbinfo); nodata: return retval; @@ -2242,14 +2250,17 @@ int sctp_verify_init(const struct sctp_association *asoc, * Returns 0 on failure, else success. * FIXME: This is an association method. */ -int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid, +int sctp_process_init(struct sctp_association *asoc, struct sctp_chunk *chunk, const union sctp_addr *peer_addr, sctp_init_chunk_t *peer_init, gfp_t gfp) { union sctp_params param; struct sctp_transport *transport; struct list_head *pos, *temp; + struct sctp_af *af; + union sctp_addr addr; char *cookie; + int src_match = 0; /* We must include the address that the INIT packet came from. * This is the only address that matters for an INIT packet. @@ -2261,18 +2272,31 @@ int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid, * added as the primary transport. The source address seems to * be a a better choice than any of the embedded addresses. */ - if (peer_addr) { - if(!sctp_assoc_add_peer(asoc, peer_addr, gfp, SCTP_ACTIVE)) - goto nomem; - } + if(!sctp_assoc_add_peer(asoc, peer_addr, gfp, SCTP_ACTIVE)) + goto nomem; + + if (sctp_cmp_addr_exact(sctp_source(chunk), peer_addr)) + src_match = 1; /* Process the initialization parameters. */ sctp_walk_params(param, peer_init, init_hdr.params) { + if (!src_match && (param.p->type == SCTP_PARAM_IPV4_ADDRESS || + param.p->type == SCTP_PARAM_IPV6_ADDRESS)) { + af = sctp_get_af_specific(param_type2af(param.p->type)); + af->from_addr_param(&addr, param.addr, + chunk->sctp_hdr->source, 0); + if (sctp_cmp_addr_exact(sctp_source(chunk), &addr)) + src_match = 1; + } if (!sctp_process_param(asoc, param, peer_addr, gfp)) goto clean_up; } + /* source address of chunk may not match any valid address */ + if (!src_match) + goto clean_up; + /* AUTH: After processing the parameters, make sure that we * have all the required info to potentially do authentications. */ @@ -2744,6 +2768,7 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc, int addr_param_len = 0; int totallen = 0; int i; + int del_pickup = 0; /* Get total length of all the address parameters. */ addr_buf = addrs; @@ -2756,6 +2781,13 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc, totallen += addr_param_len; addr_buf += af->sockaddr_len; + if (asoc->asconf_addr_del_pending && !del_pickup) { + /* reuse the parameter length from the same scope one */ + totallen += paramlen; + totallen += addr_param_len; + del_pickup = 1; + SCTP_DEBUG_PRINTK("mkasconf_update_ip: picked same-scope del_pending addr, totallen for all addresses is %d\n", totallen); + } } /* Create an asconf chunk with the required length. */ @@ -2778,6 +2810,17 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc, addr_buf += af->sockaddr_len; } + if (flags == SCTP_PARAM_ADD_IP && del_pickup) { + addr = asoc->asconf_addr_del_pending; + af = sctp_get_af_specific(addr->v4.sin_family); + addr_param_len = af->to_addr_param(addr, &addr_param); + param.param_hdr.type = SCTP_PARAM_DEL_IP; + param.param_hdr.length = htons(paramlen + addr_param_len); + param.crr_id = i; + + sctp_addto_chunk(retval, paramlen, ¶m); + sctp_addto_chunk(retval, addr_param_len, &addr_param); + } return retval; } @@ -2990,7 +3033,7 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc, * an Error Cause TLV set to the new error code 'Request to * Delete Source IP Address' */ - if (sctp_cmp_addr_exact(sctp_source(asconf), &addr)) + if (sctp_cmp_addr_exact(&asconf->source, &addr)) return SCTP_ERROR_DEL_SRC_IP; /* Section 4.2.2 @@ -3200,6 +3243,11 @@ static void sctp_asconf_param_success(struct sctp_association *asoc, case SCTP_PARAM_DEL_IP: local_bh_disable(); sctp_del_bind_addr(bp, &addr); + if (asoc->asconf_addr_del_pending != NULL && + sctp_cmp_addr_exact(asoc->asconf_addr_del_pending, &addr)) { + kfree(asoc->asconf_addr_del_pending); + asoc->asconf_addr_del_pending = NULL; + } local_bh_enable(); list_for_each_entry(transport, &asoc->peer.transport_addr_list, transports) { @@ -3357,6 +3405,9 @@ int sctp_process_asconf_ack(struct sctp_association *asoc, asconf_len -= length; } + if (no_err && asoc->src_out_of_asoc_ok) + asoc->src_out_of_asoc_ok = 0; + /* Free the cached last sent asconf chunk. */ list_del_init(&asconf->transmitted_list); sctp_chunk_free(asconf); diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 3b80fe24dab..534c2e5feb0 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -595,8 +595,7 @@ static int sctp_cmd_process_init(sctp_cmd_seq_t *commands, * fail during INIT processing (due to malloc problems), * just return the error and stop processing the stack. */ - if (!sctp_process_init(asoc, chunk->chunk_hdr->type, - sctp_source(chunk), peer_init, gfp)) + if (!sctp_process_init(asoc, chunk, sctp_source(chunk), peer_init, gfp)) error = -ENOMEM; else error = 0; @@ -1671,6 +1670,9 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, case SCTP_CMD_SEND_NEXT_ASCONF: sctp_cmd_send_asconf(asoc); break; + case SCTP_CMD_PURGE_ASCONF_QUEUE: + sctp_asconf_queue_teardown(asoc); + break; default: pr_warn("Impossible command: %u, %p\n", cmd->verb, cmd->obj.ptr); diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index c08547270e8..a297283154d 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -393,8 +393,7 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep, goto nomem_init; /* The call, sctp_process_init(), can fail on memory allocation. */ - if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type, - sctp_source(chunk), + if (!sctp_process_init(new_asoc, chunk, sctp_source(chunk), (sctp_init_chunk_t *)chunk->chunk_hdr, GFP_ATOMIC)) goto nomem_init; @@ -725,7 +724,7 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const struct sctp_endpoint *ep, */ peer_init = &chunk->subh.cookie_hdr->c.peer_init[0]; - if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type, + if (!sctp_process_init(new_asoc, chunk, &chunk->subh.cookie_hdr->c.peer_addr, peer_init, GFP_ATOMIC)) goto nomem_init; @@ -942,18 +941,9 @@ static sctp_disposition_t sctp_sf_heartbeat(const struct sctp_endpoint *ep, { struct sctp_transport *transport = (struct sctp_transport *) arg; struct sctp_chunk *reply; - sctp_sender_hb_info_t hbinfo; - size_t paylen = 0; - - hbinfo.param_hdr.type = SCTP_PARAM_HEARTBEAT_INFO; - hbinfo.param_hdr.length = htons(sizeof(sctp_sender_hb_info_t)); - hbinfo.daddr = transport->ipaddr; - hbinfo.sent_at = jiffies; - hbinfo.hb_nonce = transport->hb_nonce; /* Send a heartbeat to our peer. */ - paylen = sizeof(sctp_sender_hb_info_t); - reply = sctp_make_heartbeat(asoc, transport, &hbinfo, paylen); + reply = sctp_make_heartbeat(asoc, transport); if (!reply) return SCTP_DISPOSITION_NOMEM; @@ -1464,8 +1454,7 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( * Verification Tag and Peers Verification tag into a reserved * place (local tie-tag and per tie-tag) within the state cookie. */ - if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type, - sctp_source(chunk), + if (!sctp_process_init(new_asoc, chunk, sctp_source(chunk), (sctp_init_chunk_t *)chunk->chunk_hdr, GFP_ATOMIC)) goto nomem; @@ -1694,8 +1683,7 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(const struct sctp_endpoint *ep, */ peer_init = &chunk->subh.cookie_hdr->c.peer_init[0]; - if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type, - sctp_source(chunk), peer_init, + if (!sctp_process_init(new_asoc, chunk, sctp_source(chunk), peer_init, GFP_ATOMIC)) goto nomem; @@ -1730,11 +1718,21 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(const struct sctp_endpoint *ep, return SCTP_DISPOSITION_CONSUME; } - /* For now, fail any unsent/unacked data. Consider the optional - * choice of resending of this data. + /* For now, stop pending T3-rtx and SACK timers, fail any unsent/unacked + * data. Consider the optional choice of resending of this data. */ + sctp_add_cmd_sf(commands, SCTP_CMD_T3_RTX_TIMERS_STOP, SCTP_NULL()); + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_SACK)); sctp_add_cmd_sf(commands, SCTP_CMD_PURGE_OUTQUEUE, SCTP_NULL()); + /* Stop pending T4-rto timer, teardown ASCONF queue, ASCONF-ACK queue + * and ASCONF-ACK cache. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO)); + sctp_add_cmd_sf(commands, SCTP_CMD_PURGE_ASCONF_QUEUE, SCTP_NULL()); + repl = sctp_make_cookie_ack(new_asoc, chunk); if (!repl) goto nomem; @@ -1780,8 +1778,7 @@ static sctp_disposition_t sctp_sf_do_dupcook_b(const struct sctp_endpoint *ep, * side effects--it is safe to run them here. */ peer_init = &chunk->subh.cookie_hdr->c.peer_init[0]; - if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type, - sctp_source(chunk), peer_init, + if (!sctp_process_init(new_asoc, chunk, sctp_source(chunk), peer_init, GFP_ATOMIC)) goto nomem; @@ -2412,8 +2409,15 @@ static sctp_disposition_t __sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep, /* See if we have an error cause code in the chunk. */ len = ntohs(chunk->chunk_hdr->length); - if (len >= sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_errhdr)) + if (len >= sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_errhdr)) { + + sctp_errhdr_t *err; + sctp_walk_errors(err, chunk->chunk_hdr); + if ((void *)err != (void *)chunk->chunk_end) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + error = ((sctp_errhdr_t *)chunk->skb->data)->cause; + } sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, SCTP_ERROR(ECONNRESET)); /* ASSOC_FAILED will DELETE_TCB. */ @@ -3204,6 +3208,7 @@ sctp_disposition_t sctp_sf_operr_notify(const struct sctp_endpoint *ep, sctp_cmd_seq_t *commands) { struct sctp_chunk *chunk = arg; + sctp_errhdr_t *err; if (!sctp_vtag_verify(chunk, asoc)) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); @@ -3212,6 +3217,10 @@ sctp_disposition_t sctp_sf_operr_notify(const struct sctp_endpoint *ep, if (!sctp_chunk_length_valid(chunk, sizeof(sctp_operr_chunk_t))) return sctp_sf_violation_chunklen(ep, asoc, type, arg, commands); + sctp_walk_errors(err, chunk->chunk_hdr); + if ((void *)err != (void *)chunk->chunk_end) + return sctp_sf_violation_paramlen(ep, asoc, type, arg, + (void *)err, commands); sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_OPERR, SCTP_CHUNK(chunk)); @@ -3320,8 +3329,10 @@ sctp_disposition_t sctp_sf_ootb(const struct sctp_endpoint *ep, struct sctp_chunk *chunk = arg; struct sk_buff *skb = chunk->skb; sctp_chunkhdr_t *ch; + sctp_errhdr_t *err; __u8 *ch_end; int ootb_shut_ack = 0; + int ootb_cookie_ack = 0; SCTP_INC_STATS(SCTP_MIB_OUTOFBLUES); @@ -3346,6 +3357,23 @@ sctp_disposition_t sctp_sf_ootb(const struct sctp_endpoint *ep, if (SCTP_CID_ABORT == ch->type) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + /* RFC 8.4, 7) If the packet contains a "Stale cookie" ERROR + * or a COOKIE ACK the SCTP Packet should be silently + * discarded. + */ + + if (SCTP_CID_COOKIE_ACK == ch->type) + ootb_cookie_ack = 1; + + if (SCTP_CID_ERROR == ch->type) { + sctp_walk_errors(err, ch) { + if (SCTP_ERROR_STALE_COOKIE == err->cause) { + ootb_cookie_ack = 1; + break; + } + } + } + /* Report violation if chunk len overflows */ ch_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length)); if (ch_end > skb_tail_pointer(skb)) @@ -3357,6 +3385,8 @@ sctp_disposition_t sctp_sf_ootb(const struct sctp_endpoint *ep, if (ootb_shut_ack) return sctp_sf_shut_8_4_5(ep, asoc, type, arg, commands); + else if (ootb_cookie_ack) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); else return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands); } @@ -5057,6 +5087,30 @@ sctp_disposition_t sctp_sf_ignore_primitive( ***************************************************************************/ /* + * When the SCTP stack has no more user data to send or retransmit, this + * notification is given to the user. Also, at the time when a user app + * subscribes to this event, if there is no data to be sent or + * retransmit, the stack will immediately send up this notification. + */ +sctp_disposition_t sctp_sf_do_no_pending_tsn( + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + struct sctp_ulpevent *event; + + event = sctp_ulpevent_make_sender_dry_event(asoc, GFP_ATOMIC); + if (!event) + return SCTP_DISPOSITION_NOMEM; + + sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(event)); + + return SCTP_DISPOSITION_CONSUME; +} + +/* * Start the shutdown negotiation. * * From Section 9.2: diff --git a/net/sctp/sm_statetable.c b/net/sctp/sm_statetable.c index 546d4387fb3..0338dc6fdc9 100644 --- a/net/sctp/sm_statetable.c +++ b/net/sctp/sm_statetable.c @@ -107,8 +107,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, #define TYPE_SCTP_FUNC(func) {.fn = func, .name = #func} #define TYPE_SCTP_DATA { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -128,8 +126,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, } /* TYPE_SCTP_DATA */ #define TYPE_SCTP_INIT { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_do_5_1B_init), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -149,8 +145,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, } /* TYPE_SCTP_INIT */ #define TYPE_SCTP_INIT_ACK { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_do_5_2_3_initack), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -170,8 +164,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, } /* TYPE_SCTP_INIT_ACK */ #define TYPE_SCTP_SACK { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -191,8 +183,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, } /* TYPE_SCTP_SACK */ #define TYPE_SCTP_HEARTBEAT { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -213,8 +203,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, } /* TYPE_SCTP_HEARTBEAT */ #define TYPE_SCTP_HEARTBEAT_ACK { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -234,8 +222,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, } /* TYPE_SCTP_HEARTBEAT_ACK */ #define TYPE_SCTP_ABORT { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_pdiscard), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -255,8 +241,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, } /* TYPE_SCTP_ABORT */ #define TYPE_SCTP_SHUTDOWN { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -276,8 +260,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, } /* TYPE_SCTP_SHUTDOWN */ #define TYPE_SCTP_SHUTDOWN_ACK { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -297,8 +279,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, } /* TYPE_SCTP_SHUTDOWN_ACK */ #define TYPE_SCTP_ERROR { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -318,8 +298,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, } /* TYPE_SCTP_ERROR */ #define TYPE_SCTP_COOKIE_ECHO { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_do_5_1D_ce), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -339,8 +317,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, } /* TYPE_SCTP_COOKIE_ECHO */ #define TYPE_SCTP_COOKIE_ACK { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -360,8 +336,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, } /* TYPE_SCTP_COOKIE_ACK */ #define TYPE_SCTP_ECN_ECNE { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -381,8 +355,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, } /* TYPE_SCTP_ECN_ECNE */ #define TYPE_SCTP_ECN_CWR { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -402,8 +374,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, } /* TYPE_SCTP_ECN_CWR */ #define TYPE_SCTP_SHUTDOWN_COMPLETE { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -446,8 +416,6 @@ static const sctp_sm_table_entry_t chunk_event_table[SCTP_NUM_BASE_CHUNK_TYPES][ }; /* state_fn_t chunk_event_table[][] */ #define TYPE_SCTP_ASCONF { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -467,8 +435,6 @@ static const sctp_sm_table_entry_t chunk_event_table[SCTP_NUM_BASE_CHUNK_TYPES][ } /* TYPE_SCTP_ASCONF */ #define TYPE_SCTP_ASCONF_ACK { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -496,8 +462,6 @@ static const sctp_sm_table_entry_t addip_chunk_event_table[SCTP_NUM_ADDIP_CHUNK_ }; /*state_fn_t addip_chunk_event_table[][] */ #define TYPE_SCTP_FWD_TSN { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -524,8 +488,6 @@ static const sctp_sm_table_entry_t prsctp_chunk_event_table[SCTP_NUM_PRSCTP_CHUN }; /*state_fn_t prsctp_chunk_event_table[][] */ #define TYPE_SCTP_AUTH { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -553,8 +515,6 @@ static const sctp_sm_table_entry_t auth_chunk_event_table[SCTP_NUM_AUTH_CHUNK_TY static const sctp_sm_table_entry_t chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = { - /* SCTP_STATE_EMPTY */ - TYPE_SCTP_FUNC(sctp_sf_ootb), /* SCTP_STATE_CLOSED */ TYPE_SCTP_FUNC(sctp_sf_ootb), /* SCTP_STATE_COOKIE_WAIT */ @@ -575,8 +535,6 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = { #define TYPE_SCTP_PRIMITIVE_ASSOCIATE { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_do_prm_asoc), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -596,8 +554,6 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = { } /* TYPE_SCTP_PRIMITIVE_ASSOCIATE */ #define TYPE_SCTP_PRIMITIVE_SHUTDOWN { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_error_closed), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -617,8 +573,6 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = { } /* TYPE_SCTP_PRIMITIVE_SHUTDOWN */ #define TYPE_SCTP_PRIMITIVE_ABORT { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_error_closed), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -638,8 +592,6 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = { } /* TYPE_SCTP_PRIMITIVE_ABORT */ #define TYPE_SCTP_PRIMITIVE_SEND { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_error_closed), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -659,8 +611,6 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = { } /* TYPE_SCTP_PRIMITIVE_SEND */ #define TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_error_closed), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -680,8 +630,6 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = { } /* TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT */ #define TYPE_SCTP_PRIMITIVE_ASCONF { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_error_closed), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -713,8 +661,6 @@ static const sctp_sm_table_entry_t primitive_event_table[SCTP_NUM_PRIMITIVE_TYPE }; #define TYPE_SCTP_OTHER_NO_PENDING_TSN { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_ignore_other), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -722,7 +668,7 @@ static const sctp_sm_table_entry_t primitive_event_table[SCTP_NUM_PRIMITIVE_TYPE /* SCTP_STATE_COOKIE_ECHOED */ \ TYPE_SCTP_FUNC(sctp_sf_ignore_other), \ /* SCTP_STATE_ESTABLISHED */ \ - TYPE_SCTP_FUNC(sctp_sf_ignore_other), \ + TYPE_SCTP_FUNC(sctp_sf_do_no_pending_tsn), \ /* SCTP_STATE_SHUTDOWN_PENDING */ \ TYPE_SCTP_FUNC(sctp_sf_do_9_2_start_shutdown), \ /* SCTP_STATE_SHUTDOWN_SENT */ \ @@ -734,8 +680,6 @@ static const sctp_sm_table_entry_t primitive_event_table[SCTP_NUM_PRIMITIVE_TYPE } #define TYPE_SCTP_OTHER_ICMP_PROTO_UNREACH { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_ignore_other), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -760,8 +704,6 @@ static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_ }; #define TYPE_SCTP_EVENT_TIMEOUT_NONE { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -781,8 +723,6 @@ static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_ } #define TYPE_SCTP_EVENT_TIMEOUT_T1_COOKIE { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -802,8 +742,6 @@ static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_ } #define TYPE_SCTP_EVENT_TIMEOUT_T1_INIT { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -823,8 +761,6 @@ static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_ } #define TYPE_SCTP_EVENT_TIMEOUT_T2_SHUTDOWN { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -844,8 +780,6 @@ static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_ } #define TYPE_SCTP_EVENT_TIMEOUT_T3_RTX { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -865,8 +799,6 @@ static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_ } #define TYPE_SCTP_EVENT_TIMEOUT_T4_RTO { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -886,8 +818,6 @@ static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_ } #define TYPE_SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -907,8 +837,6 @@ static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_ } #define TYPE_SCTP_EVENT_TIMEOUT_HEARTBEAT { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -928,8 +856,6 @@ static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_ } #define TYPE_SCTP_EVENT_TIMEOUT_SACK { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -949,8 +875,6 @@ static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_ } #define TYPE_SCTP_EVENT_TIMEOUT_AUTOCLOSE { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \ /* SCTP_STATE_COOKIE_WAIT */ \ diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 431b8905c57..60038fef3ba 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -583,10 +583,6 @@ static int sctp_send_asconf_add_ip(struct sock *sk, goto out; } - retval = sctp_send_asconf(asoc, chunk); - if (retval) - goto out; - /* Add the new addresses to the bind address list with * use_as_src set to 0. */ @@ -599,6 +595,23 @@ static int sctp_send_asconf_add_ip(struct sock *sk, SCTP_ADDR_NEW, GFP_ATOMIC); addr_buf += af->sockaddr_len; } + if (asoc->src_out_of_asoc_ok) { + struct sctp_transport *trans; + + list_for_each_entry(trans, + &asoc->peer.transport_addr_list, transports) { + /* Clear the source and route cache */ + dst_release(trans->dst); + trans->cwnd = min(4*asoc->pathmtu, max_t(__u32, + 2*asoc->pathmtu, 4380)); + trans->ssthresh = asoc->peer.i.a_rwnd; + trans->rto = asoc->rto_initial; + trans->rtt = trans->srtt = trans->rttvar = 0; + sctp_transport_route(trans, NULL, + sctp_sk(asoc->base.sk)); + } + } + retval = sctp_send_asconf(asoc, chunk); } out: @@ -715,7 +728,9 @@ static int sctp_send_asconf_del_ip(struct sock *sk, struct sctp_sockaddr_entry *saddr; int i; int retval = 0; + int stored = 0; + chunk = NULL; if (!sctp_addip_enable) return retval; @@ -766,8 +781,37 @@ static int sctp_send_asconf_del_ip(struct sock *sk, bp = &asoc->base.bind_addr; laddr = sctp_find_unmatch_addr(bp, (union sctp_addr *)addrs, addrcnt, sp); - if (!laddr) - continue; + if ((laddr == NULL) && (addrcnt == 1)) { + if (asoc->asconf_addr_del_pending) + continue; + asoc->asconf_addr_del_pending = + kzalloc(sizeof(union sctp_addr), GFP_ATOMIC); + if (asoc->asconf_addr_del_pending == NULL) { + retval = -ENOMEM; + goto out; + } + asoc->asconf_addr_del_pending->sa.sa_family = + addrs->sa_family; + asoc->asconf_addr_del_pending->v4.sin_port = + htons(bp->port); + if (addrs->sa_family == AF_INET) { + struct sockaddr_in *sin; + + sin = (struct sockaddr_in *)addrs; + asoc->asconf_addr_del_pending->v4.sin_addr.s_addr = sin->sin_addr.s_addr; + } else if (addrs->sa_family == AF_INET6) { + struct sockaddr_in6 *sin6; + + sin6 = (struct sockaddr_in6 *)addrs; + ipv6_addr_copy(&asoc->asconf_addr_del_pending->v6.sin6_addr, &sin6->sin6_addr); + } + SCTP_DEBUG_PRINTK_IPADDR("send_asconf_del_ip: keep the last address asoc: %p ", + " at %p\n", asoc, asoc->asconf_addr_del_pending, + asoc->asconf_addr_del_pending); + asoc->src_out_of_asoc_ok = 1; + stored = 1; + goto skip_mkasconf; + } /* We do not need RCU protection throughout this loop * because this is done under a socket lock from the @@ -780,6 +824,7 @@ static int sctp_send_asconf_del_ip(struct sock *sk, goto out; } +skip_mkasconf: /* Reset use_as_src flag for the addresses in the bind address * list that are to be deleted. */ @@ -805,12 +850,37 @@ static int sctp_send_asconf_del_ip(struct sock *sk, sctp_sk(asoc->base.sk)); } + if (stored) + /* We don't need to transmit ASCONF */ + continue; retval = sctp_send_asconf(asoc, chunk); } out: return retval; } +/* set addr events to assocs in the endpoint. ep and addr_wq must be locked */ +int sctp_asconf_mgmt(struct sctp_sock *sp, struct sctp_sockaddr_entry *addrw) +{ + struct sock *sk = sctp_opt2sk(sp); + union sctp_addr *addr; + struct sctp_af *af; + + /* It is safe to write port space in caller. */ + addr = &addrw->a; + addr->v4.sin_port = htons(sp->ep->base.bind_addr.port); + af = sctp_get_af_specific(addr->sa.sa_family); + if (!af) + return -EINVAL; + if (sctp_verify_addr(sk, addr, af->sockaddr_len)) + return -EINVAL; + + if (addrw->state == SCTP_ADDR_NEW) + return sctp_send_asconf_add_ip(sk, (struct sockaddr *)addr, 1); + else + return sctp_send_asconf_del_ip(sk, (struct sockaddr *)addr, 1); +} + /* Helper for tunneling sctp_bindx() requests through sctp_setsockopt() * * API 8.1 @@ -1496,7 +1566,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, struct sctp_chunk *chunk; union sctp_addr to; struct sockaddr *msg_name = NULL; - struct sctp_sndrcvinfo default_sinfo = { 0 }; + struct sctp_sndrcvinfo default_sinfo; struct sctp_sndrcvinfo *sinfo; struct sctp_initmsg *sinit; sctp_assoc_t associd = 0; @@ -1760,6 +1830,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, /* If the user didn't specify SNDRCVINFO, make up one with * some defaults. */ + memset(&default_sinfo, 0, sizeof(default_sinfo)); default_sinfo.sinfo_stream = asoc->default_stream; default_sinfo.sinfo_flags = asoc->default_flags; default_sinfo.sinfo_ppid = asoc->default_ppid; @@ -1790,12 +1861,10 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, goto out_free; } - if (sinfo) { - /* Check for invalid stream. */ - if (sinfo->sinfo_stream >= asoc->c.sinit_num_ostreams) { - err = -EINVAL; - goto out_free; - } + /* Check for invalid stream. */ + if (sinfo->sinfo_stream >= asoc->c.sinit_num_ostreams) { + err = -EINVAL; + goto out_free; } timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT); @@ -2287,7 +2356,7 @@ static int sctp_apply_peer_addr_params(struct sctp_paddrparams *params, trans->param_flags = (trans->param_flags & ~SPP_PMTUD) | pmtud_change; if (update) { - sctp_transport_pmtu(trans); + sctp_transport_pmtu(trans, sctp_opt2sk(sp)); sctp_assoc_sync_pmtu(asoc); } } else if (asoc) { @@ -3335,6 +3404,46 @@ static int sctp_setsockopt_del_key(struct sock *sk, } +/* + * 8.1.23 SCTP_AUTO_ASCONF + * + * This option will enable or disable the use of the automatic generation of + * ASCONF chunks to add and delete addresses to an existing association. Note + * that this option has two caveats namely: a) it only affects sockets that + * are bound to all addresses available to the SCTP stack, and b) the system + * administrator may have an overriding control that turns the ASCONF feature + * off no matter what setting the socket option may have. + * This option expects an integer boolean flag, where a non-zero value turns on + * the option, and a zero value turns off the option. + * Note. In this implementation, socket operation overrides default parameter + * being set by sysctl as well as FreeBSD implementation + */ +static int sctp_setsockopt_auto_asconf(struct sock *sk, char __user *optval, + unsigned int optlen) +{ + int val; + struct sctp_sock *sp = sctp_sk(sk); + + if (optlen < sizeof(int)) + return -EINVAL; + if (get_user(val, (int __user *)optval)) + return -EFAULT; + if (!sctp_is_ep_boundall(sk) && val) + return -EINVAL; + if ((val && sp->do_auto_asconf) || (!val && !sp->do_auto_asconf)) + return 0; + + if (val == 0 && sp->do_auto_asconf) { + list_del(&sp->auto_asconf_list); + sp->do_auto_asconf = 0; + } else if (val && !sp->do_auto_asconf) { + list_add_tail(&sp->auto_asconf_list, + &sctp_auto_asconf_splist); + sp->do_auto_asconf = 1; + } + return 0; +} + /* API 6.2 setsockopt(), getsockopt() * @@ -3482,6 +3591,9 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname, case SCTP_AUTH_DELETE_KEY: retval = sctp_setsockopt_del_key(sk, optval, optlen); break; + case SCTP_AUTO_ASCONF: + retval = sctp_setsockopt_auto_asconf(sk, optval, optlen); + break; default: retval = -ENOPROTOOPT; break; @@ -3764,6 +3876,12 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk) local_bh_disable(); percpu_counter_inc(&sctp_sockets_allocated); sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); + if (sctp_default_auto_asconf) { + list_add_tail(&sp->auto_asconf_list, + &sctp_auto_asconf_splist); + sp->do_auto_asconf = 1; + } else + sp->do_auto_asconf = 0; local_bh_enable(); return 0; @@ -3772,13 +3890,17 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk) /* Cleanup any SCTP per socket resources. */ SCTP_STATIC void sctp_destroy_sock(struct sock *sk) { - struct sctp_endpoint *ep; + struct sctp_sock *sp; SCTP_DEBUG_PRINTK("sctp_destroy_sock(sk: %p)\n", sk); /* Release our hold on the endpoint. */ - ep = sctp_sk(sk)->ep; - sctp_endpoint_free(ep); + sp = sctp_sk(sk); + if (sp->do_auto_asconf) { + sp->do_auto_asconf = 0; + list_del(&sp->auto_asconf_list); + } + sctp_endpoint_free(sp->ep); local_bh_disable(); percpu_counter_dec(&sctp_sockets_allocated); sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); @@ -5277,6 +5399,77 @@ static int sctp_getsockopt_assoc_number(struct sock *sk, int len, return 0; } +/* + * 8.1.23 SCTP_AUTO_ASCONF + * See the corresponding setsockopt entry as description + */ +static int sctp_getsockopt_auto_asconf(struct sock *sk, int len, + char __user *optval, int __user *optlen) +{ + int val = 0; + + if (len < sizeof(int)) + return -EINVAL; + + len = sizeof(int); + if (sctp_sk(sk)->do_auto_asconf && sctp_is_ep_boundall(sk)) + val = 1; + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, &val, len)) + return -EFAULT; + return 0; +} + +/* + * 8.2.6. Get the Current Identifiers of Associations + * (SCTP_GET_ASSOC_ID_LIST) + * + * This option gets the current list of SCTP association identifiers of + * the SCTP associations handled by a one-to-many style socket. + */ +static int sctp_getsockopt_assoc_ids(struct sock *sk, int len, + char __user *optval, int __user *optlen) +{ + struct sctp_sock *sp = sctp_sk(sk); + struct sctp_association *asoc; + struct sctp_assoc_ids *ids; + u32 num = 0; + + if (sctp_style(sk, TCP)) + return -EOPNOTSUPP; + + if (len < sizeof(struct sctp_assoc_ids)) + return -EINVAL; + + list_for_each_entry(asoc, &(sp->ep->asocs), asocs) { + num++; + } + + if (len < sizeof(struct sctp_assoc_ids) + sizeof(sctp_assoc_t) * num) + return -EINVAL; + + len = sizeof(struct sctp_assoc_ids) + sizeof(sctp_assoc_t) * num; + + ids = kmalloc(len, GFP_KERNEL); + if (unlikely(!ids)) + return -ENOMEM; + + ids->gaids_number_of_ids = num; + num = 0; + list_for_each_entry(asoc, &(sp->ep->asocs), asocs) { + ids->gaids_assoc_id[num++] = asoc->assoc_id; + } + + if (put_user(len, optlen) || copy_to_user(optval, ids, len)) { + kfree(ids); + return -EFAULT; + } + + kfree(ids); + return 0; +} + SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen) { @@ -5409,6 +5602,12 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, case SCTP_GET_ASSOC_NUMBER: retval = sctp_getsockopt_assoc_number(sk, len, optval, optlen); break; + case SCTP_GET_ASSOC_ID_LIST: + retval = sctp_getsockopt_assoc_ids(sk, len, optval, optlen); + break; + case SCTP_AUTO_ASCONF: + retval = sctp_getsockopt_auto_asconf(sk, len, optval, optlen); + break; default: retval = -ENOPROTOOPT; break; @@ -6461,6 +6660,7 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, struct sk_buff *skb, *tmp; struct sctp_ulpevent *event; struct sctp_bind_hashbucket *head; + struct list_head tmplist; /* Migrate socket buffer sizes and all the socket level options to the * new socket. @@ -6468,7 +6668,12 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, newsk->sk_sndbuf = oldsk->sk_sndbuf; newsk->sk_rcvbuf = oldsk->sk_rcvbuf; /* Brute force copy old sctp opt. */ - inet_sk_copy_descendant(newsk, oldsk); + if (oldsp->do_auto_asconf) { + memcpy(&tmplist, &newsp->auto_asconf_list, sizeof(tmplist)); + inet_sk_copy_descendant(newsk, oldsk); + memcpy(&newsp->auto_asconf_list, &tmplist, sizeof(tmplist)); + } else + inet_sk_copy_descendant(newsk, oldsk); /* Restore the ep value that was overwritten with the above structure * copy. diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c index 50cb57f0919..6b3952961b8 100644 --- a/net/sctp/sysctl.c +++ b/net/sctp/sysctl.c @@ -183,6 +183,13 @@ static ctl_table sctp_table[] = { .proc_handler = proc_dointvec, }, { + .procname = "default_auto_asconf", + .data = &sctp_default_auto_asconf, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + { .procname = "prsctp_enable", .data = &sctp_prsctp_enable, .maxlen = sizeof(int), diff --git a/net/sctp/transport.c b/net/sctp/transport.c index d3ae493d234..394c57ca2f5 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -211,15 +211,17 @@ void sctp_transport_set_owner(struct sctp_transport *transport, } /* Initialize the pmtu of a transport. */ -void sctp_transport_pmtu(struct sctp_transport *transport) +void sctp_transport_pmtu(struct sctp_transport *transport, struct sock *sk) { - struct dst_entry *dst; - - dst = transport->af_specific->get_dst(NULL, &transport->ipaddr, NULL); + /* If we don't have a fresh route, look one up */ + if (!transport->dst || transport->dst->obsolete > 1) { + dst_release(transport->dst); + transport->af_specific->get_dst(transport, &transport->saddr, + &transport->fl, sk); + } - if (dst) { - transport->pathmtu = dst_mtu(dst); - dst_release(dst); + if (transport->dst) { + transport->pathmtu = dst_mtu(transport->dst); } else transport->pathmtu = SCTP_DEFAULT_MAXSEGMENT; } @@ -270,22 +272,19 @@ void sctp_transport_route(struct sctp_transport *transport, { struct sctp_association *asoc = transport->asoc; struct sctp_af *af = transport->af_specific; - union sctp_addr *daddr = &transport->ipaddr; - struct dst_entry *dst; - dst = af->get_dst(asoc, daddr, saddr); + af->get_dst(transport, saddr, &transport->fl, sctp_opt2sk(opt)); if (saddr) memcpy(&transport->saddr, saddr, sizeof(union sctp_addr)); else - af->get_saddr(opt, asoc, dst, daddr, &transport->saddr); + af->get_saddr(opt, transport, &transport->fl); - transport->dst = dst; if ((transport->param_flags & SPP_PMTUD_DISABLE) && transport->pathmtu) { return; } - if (dst) { - transport->pathmtu = dst_mtu(dst); + if (transport->dst) { + transport->pathmtu = dst_mtu(transport->dst); /* Initialize sk->sk_rcv_saddr, if the transport is the * association's active path for getsockname(). diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c index dff27d5e22f..e70e5fc8789 100644 --- a/net/sctp/ulpevent.c +++ b/net/sctp/ulpevent.c @@ -554,7 +554,7 @@ struct sctp_ulpevent *sctp_ulpevent_make_send_failed( memcpy(&ssf->ssf_info, &chunk->sinfo, sizeof(struct sctp_sndrcvinfo)); /* Per TSVWG discussion with Randy. Allow the application to - * resemble a fragmented message. + * reassemble a fragmented message. */ ssf->ssf_info.sinfo_flags = chunk->chunk_hdr->flags; @@ -843,7 +843,7 @@ struct sctp_ulpevent *sctp_ulpevent_make_authkey( ak = (struct sctp_authkey_event *) skb_put(skb, sizeof(struct sctp_authkey_event)); - ak->auth_type = SCTP_AUTHENTICATION_INDICATION; + ak->auth_type = SCTP_AUTHENTICATION_EVENT; ak->auth_flags = 0; ak->auth_length = sizeof(struct sctp_authkey_event); @@ -862,6 +862,34 @@ fail: return NULL; } +/* + * Socket Extensions for SCTP + * 6.3.10. SCTP_SENDER_DRY_EVENT + */ +struct sctp_ulpevent *sctp_ulpevent_make_sender_dry_event( + const struct sctp_association *asoc, gfp_t gfp) +{ + struct sctp_ulpevent *event; + struct sctp_sender_dry_event *sdry; + struct sk_buff *skb; + + event = sctp_ulpevent_new(sizeof(struct sctp_sender_dry_event), + MSG_NOTIFICATION, gfp); + if (!event) + return NULL; + + skb = sctp_event2skb(event); + sdry = (struct sctp_sender_dry_event *) + skb_put(skb, sizeof(struct sctp_sender_dry_event)); + + sdry->sender_dry_type = SCTP_SENDER_DRY_EVENT; + sdry->sender_dry_flags = 0; + sdry->sender_dry_length = sizeof(struct sctp_sender_dry_event); + sctp_ulpevent_set_owner(event, asoc); + sdry->sender_dry_assoc_id = sctp_assoc2id(asoc); + + return event; +} /* Return the notification type, assuming this is a notification * event. diff --git a/net/socket.c b/net/socket.c index d25f5a9d6fa..02dc82db3d2 100644 --- a/net/socket.c +++ b/net/socket.c @@ -263,15 +263,6 @@ static struct inode *sock_alloc_inode(struct super_block *sb) return &ei->vfs_inode; } - - -static void wq_free_rcu(struct rcu_head *head) -{ - struct socket_wq *wq = container_of(head, struct socket_wq, rcu); - - kfree(wq); -} - static void sock_destroy_inode(struct inode *inode) { struct socket_alloc *ei; @@ -279,7 +270,7 @@ static void sock_destroy_inode(struct inode *inode) ei = container_of(inode, struct socket_alloc, vfs_inode); wq = rcu_dereference_protected(ei->socket.wq, 1); - call_rcu(&wq->rcu, wq_free_rcu); + kfree_rcu(wq, rcu); kmem_cache_free(sock_inode_cachep, ei); } @@ -551,11 +542,10 @@ int sock_tx_timestamp(struct sock *sk, __u8 *tx_flags) } EXPORT_SYMBOL(sock_tx_timestamp); -static inline int __sock_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size) +static inline int __sock_sendmsg_nosec(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t size) { struct sock_iocb *si = kiocb_to_siocb(iocb); - int err; sock_update_classid(sock->sk); @@ -564,13 +554,17 @@ static inline int __sock_sendmsg(struct kiocb *iocb, struct socket *sock, si->msg = msg; si->size = size; - err = security_socket_sendmsg(sock, msg, size); - if (err) - return err; - return sock->ops->sendmsg(iocb, sock, msg, size); } +static inline int __sock_sendmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t size) +{ + int err = security_socket_sendmsg(sock, msg, size); + + return err ?: __sock_sendmsg_nosec(iocb, sock, msg, size); +} + int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) { struct kiocb iocb; @@ -586,6 +580,20 @@ int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) } EXPORT_SYMBOL(sock_sendmsg); +int sock_sendmsg_nosec(struct socket *sock, struct msghdr *msg, size_t size) +{ + struct kiocb iocb; + struct sock_iocb siocb; + int ret; + + init_sync_kiocb(&iocb, NULL); + iocb.private = &siocb; + ret = __sock_sendmsg_nosec(&iocb, sock, msg, size); + if (-EIOCBQUEUED == ret) + ret = wait_on_sync_kiocb(&iocb); + return ret; +} + int kernel_sendmsg(struct socket *sock, struct msghdr *msg, struct kvec *vec, size_t num, size_t size) { @@ -1863,57 +1871,47 @@ SYSCALL_DEFINE2(shutdown, int, fd, int, how) #define COMPAT_NAMELEN(msg) COMPAT_MSG(msg, msg_namelen) #define COMPAT_FLAGS(msg) COMPAT_MSG(msg, msg_flags) -/* - * BSD sendmsg interface - */ - -SYSCALL_DEFINE3(sendmsg, int, fd, struct msghdr __user *, msg, unsigned, flags) +static int __sys_sendmsg(struct socket *sock, struct msghdr __user *msg, + struct msghdr *msg_sys, unsigned flags, int nosec) { struct compat_msghdr __user *msg_compat = (struct compat_msghdr __user *)msg; - struct socket *sock; struct sockaddr_storage address; struct iovec iovstack[UIO_FASTIOV], *iov = iovstack; unsigned char ctl[sizeof(struct cmsghdr) + 20] __attribute__ ((aligned(sizeof(__kernel_size_t)))); /* 20 is size of ipv6_pktinfo */ unsigned char *ctl_buf = ctl; - struct msghdr msg_sys; int err, ctl_len, iov_size, total_len; - int fput_needed; err = -EFAULT; if (MSG_CMSG_COMPAT & flags) { - if (get_compat_msghdr(&msg_sys, msg_compat)) + if (get_compat_msghdr(msg_sys, msg_compat)) return -EFAULT; - } else if (copy_from_user(&msg_sys, msg, sizeof(struct msghdr))) + } else if (copy_from_user(msg_sys, msg, sizeof(struct msghdr))) return -EFAULT; - sock = sockfd_lookup_light(fd, &err, &fput_needed); - if (!sock) - goto out; - /* do not move before msg_sys is valid */ err = -EMSGSIZE; - if (msg_sys.msg_iovlen > UIO_MAXIOV) - goto out_put; + if (msg_sys->msg_iovlen > UIO_MAXIOV) + goto out; /* Check whether to allocate the iovec area */ err = -ENOMEM; - iov_size = msg_sys.msg_iovlen * sizeof(struct iovec); - if (msg_sys.msg_iovlen > UIO_FASTIOV) { + iov_size = msg_sys->msg_iovlen * sizeof(struct iovec); + if (msg_sys->msg_iovlen > UIO_FASTIOV) { iov = sock_kmalloc(sock->sk, iov_size, GFP_KERNEL); if (!iov) - goto out_put; + goto out; } /* This will also move the address data into kernel space */ if (MSG_CMSG_COMPAT & flags) { - err = verify_compat_iovec(&msg_sys, iov, + err = verify_compat_iovec(msg_sys, iov, (struct sockaddr *)&address, VERIFY_READ); } else - err = verify_iovec(&msg_sys, iov, + err = verify_iovec(msg_sys, iov, (struct sockaddr *)&address, VERIFY_READ); if (err < 0) @@ -1922,17 +1920,17 @@ SYSCALL_DEFINE3(sendmsg, int, fd, struct msghdr __user *, msg, unsigned, flags) err = -ENOBUFS; - if (msg_sys.msg_controllen > INT_MAX) + if (msg_sys->msg_controllen > INT_MAX) goto out_freeiov; - ctl_len = msg_sys.msg_controllen; + ctl_len = msg_sys->msg_controllen; if ((MSG_CMSG_COMPAT & flags) && ctl_len) { err = - cmsghdr_from_user_compat_to_kern(&msg_sys, sock->sk, ctl, + cmsghdr_from_user_compat_to_kern(msg_sys, sock->sk, ctl, sizeof(ctl)); if (err) goto out_freeiov; - ctl_buf = msg_sys.msg_control; - ctl_len = msg_sys.msg_controllen; + ctl_buf = msg_sys->msg_control; + ctl_len = msg_sys->msg_controllen; } else if (ctl_len) { if (ctl_len > sizeof(ctl)) { ctl_buf = sock_kmalloc(sock->sk, ctl_len, GFP_KERNEL); @@ -1941,21 +1939,22 @@ SYSCALL_DEFINE3(sendmsg, int, fd, struct msghdr __user *, msg, unsigned, flags) } err = -EFAULT; /* - * Careful! Before this, msg_sys.msg_control contains a user pointer. + * Careful! Before this, msg_sys->msg_control contains a user pointer. * Afterwards, it will be a kernel pointer. Thus the compiler-assisted * checking falls down on this. */ if (copy_from_user(ctl_buf, - (void __user __force *)msg_sys.msg_control, + (void __user __force *)msg_sys->msg_control, ctl_len)) goto out_freectl; - msg_sys.msg_control = ctl_buf; + msg_sys->msg_control = ctl_buf; } - msg_sys.msg_flags = flags; + msg_sys->msg_flags = flags; if (sock->file->f_flags & O_NONBLOCK) - msg_sys.msg_flags |= MSG_DONTWAIT; - err = sock_sendmsg(sock, &msg_sys, total_len); + msg_sys->msg_flags |= MSG_DONTWAIT; + err = (nosec ? sock_sendmsg_nosec : sock_sendmsg)(sock, msg_sys, + total_len); out_freectl: if (ctl_buf != ctl) @@ -1963,12 +1962,114 @@ out_freectl: out_freeiov: if (iov != iovstack) sock_kfree_s(sock->sk, iov, iov_size); -out_put: +out: + return err; +} + +/* + * BSD sendmsg interface + */ + +SYSCALL_DEFINE3(sendmsg, int, fd, struct msghdr __user *, msg, unsigned, flags) +{ + int fput_needed, err; + struct msghdr msg_sys; + struct socket *sock = sockfd_lookup_light(fd, &err, &fput_needed); + + if (!sock) + goto out; + + err = __sys_sendmsg(sock, msg, &msg_sys, flags, 0); + fput_light(sock->file, fput_needed); out: return err; } +/* + * Linux sendmmsg interface + */ + +int __sys_sendmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen, + unsigned int flags) +{ + int fput_needed, err, datagrams; + struct socket *sock; + struct mmsghdr __user *entry; + struct compat_mmsghdr __user *compat_entry; + struct msghdr msg_sys; + + datagrams = 0; + + sock = sockfd_lookup_light(fd, &err, &fput_needed); + if (!sock) + return err; + + err = sock_error(sock->sk); + if (err) + goto out_put; + + entry = mmsg; + compat_entry = (struct compat_mmsghdr __user *)mmsg; + + while (datagrams < vlen) { + /* + * No need to ask LSM for more than the first datagram. + */ + if (MSG_CMSG_COMPAT & flags) { + err = __sys_sendmsg(sock, (struct msghdr __user *)compat_entry, + &msg_sys, flags, datagrams); + if (err < 0) + break; + err = __put_user(err, &compat_entry->msg_len); + ++compat_entry; + } else { + err = __sys_sendmsg(sock, (struct msghdr __user *)entry, + &msg_sys, flags, datagrams); + if (err < 0) + break; + err = put_user(err, &entry->msg_len); + ++entry; + } + + if (err) + break; + ++datagrams; + } + +out_put: + fput_light(sock->file, fput_needed); + + if (err == 0) + return datagrams; + + if (datagrams != 0) { + /* + * We may send less entries than requested (vlen) if the + * sock is non blocking... + */ + if (err != -EAGAIN) { + /* + * ... or if sendmsg returns an error after we + * send some datagrams, where we record the + * error to return on the next call or if the + * app asks about it using getsockopt(SO_ERROR). + */ + sock->sk->sk_err = -err; + } + + return datagrams; + } + + return err; +} + +SYSCALL_DEFINE4(sendmmsg, int, fd, struct mmsghdr __user *, mmsg, + unsigned int, vlen, unsigned int, flags) +{ + return __sys_sendmmsg(fd, mmsg, vlen, flags); +} + static int __sys_recvmsg(struct socket *sock, struct msghdr __user *msg, struct msghdr *msg_sys, unsigned flags, int nosec) { @@ -2122,14 +2223,16 @@ int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen, */ if (MSG_CMSG_COMPAT & flags) { err = __sys_recvmsg(sock, (struct msghdr __user *)compat_entry, - &msg_sys, flags, datagrams); + &msg_sys, flags & ~MSG_WAITFORONE, + datagrams); if (err < 0) break; err = __put_user(err, &compat_entry->msg_len); ++compat_entry; } else { err = __sys_recvmsg(sock, (struct msghdr __user *)entry, - &msg_sys, flags, datagrams); + &msg_sys, flags & ~MSG_WAITFORONE, + datagrams); if (err < 0) break; err = put_user(err, &entry->msg_len); @@ -2214,11 +2317,11 @@ SYSCALL_DEFINE5(recvmmsg, int, fd, struct mmsghdr __user *, mmsg, #ifdef __ARCH_WANT_SYS_SOCKETCALL /* Argument list sizes for sys_socketcall */ #define AL(x) ((x) * sizeof(unsigned long)) -static const unsigned char nargs[20] = { +static const unsigned char nargs[21] = { AL(0), AL(3), AL(3), AL(3), AL(2), AL(3), AL(3), AL(3), AL(4), AL(4), AL(4), AL(6), AL(6), AL(2), AL(5), AL(5), AL(3), AL(3), - AL(4), AL(5) + AL(4), AL(5), AL(4) }; #undef AL @@ -2238,7 +2341,7 @@ SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args) int err; unsigned int len; - if (call < 1 || call > SYS_RECVMMSG) + if (call < 1 || call > SYS_SENDMMSG) return -EINVAL; len = nargs[call]; @@ -2313,6 +2416,9 @@ SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args) case SYS_SENDMSG: err = sys_sendmsg(a0, (struct msghdr __user *)a1, a[2]); break; + case SYS_SENDMMSG: + err = sys_sendmmsg(a0, (struct mmsghdr __user *)a1, a[2], a[3]); + break; case SYS_RECVMSG: err = sys_recvmsg(a0, (struct msghdr __user *)a1, a[2]); break; diff --git a/net/sunrpc/Kconfig b/net/sunrpc/Kconfig index 8873fd8ddac..b2198e65d8b 100644 --- a/net/sunrpc/Kconfig +++ b/net/sunrpc/Kconfig @@ -18,14 +18,13 @@ config SUNRPC_XPRT_RDMA If unsure, say N. config RPCSEC_GSS_KRB5 - tristate + tristate "Secure RPC: Kerberos V mechanism" depends on SUNRPC && CRYPTO - prompt "Secure RPC: Kerberos V mechanism" if !(NFS_V4 || NFSD_V4) + depends on CRYPTO_MD5 && CRYPTO_DES && CRYPTO_CBC && CRYPTO_CTS + depends on CRYPTO_ECB && CRYPTO_HMAC && CRYPTO_SHA1 && CRYPTO_AES + depends on CRYPTO_ARC4 default y select SUNRPC_GSS - select CRYPTO_MD5 - select CRYPTO_DES - select CRYPTO_CBC help Choose Y here to enable Secure RPC using the Kerberos version 5 GSS-API mechanism (RFC 1964). diff --git a/net/sunrpc/addr.c b/net/sunrpc/addr.c index 1419d0cdbba..4195233c491 100644 --- a/net/sunrpc/addr.c +++ b/net/sunrpc/addr.c @@ -151,7 +151,7 @@ static size_t rpc_pton4(const char *buf, const size_t buflen, return 0; sin->sin_family = AF_INET; - return sizeof(struct sockaddr_in);; + return sizeof(struct sockaddr_in); } #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index 67e31276682..727e506cacd 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -326,10 +326,12 @@ rpcauth_prune_expired(struct list_head *free, int nr_to_scan) * Run memory cache shrinker. */ static int -rpcauth_cache_shrinker(struct shrinker *shrink, int nr_to_scan, gfp_t gfp_mask) +rpcauth_cache_shrinker(struct shrinker *shrink, struct shrink_control *sc) { LIST_HEAD(free); int res; + int nr_to_scan = sc->nr_to_scan; + gfp_t gfp_mask = sc->gfp_mask; if ((gfp_mask & GFP_KERNEL) != GFP_KERNEL) return (nr_to_scan == 0) ? 0 : -1; @@ -624,7 +626,7 @@ rpcauth_refreshcred(struct rpc_task *task) if (err < 0) goto out; cred = task->tk_rqstp->rq_cred; - }; + } dprintk("RPC: %5u refreshing %s cred %p\n", task->tk_pid, cred->cr_auth->au_ops->au_name, cred); diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index f3914d0c507..339ba64cce1 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -520,7 +520,7 @@ gss_refresh_upcall(struct rpc_task *task) warn_gssd(); task->tk_timeout = 15*HZ; rpc_sleep_on(&pipe_version_rpc_waitqueue, task, NULL); - return 0; + return -EAGAIN; } if (IS_ERR(gss_msg)) { err = PTR_ERR(gss_msg); @@ -563,10 +563,12 @@ retry: if (PTR_ERR(gss_msg) == -EAGAIN) { err = wait_event_interruptible_timeout(pipe_version_waitqueue, pipe_version >= 0, 15*HZ); + if (pipe_version < 0) { + warn_gssd(); + err = -EACCES; + } if (err) goto out; - if (pipe_version < 0) - warn_gssd(); goto retry; } if (IS_ERR(gss_msg)) { diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index e7a96e478f6..b84d7395535 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -13,10 +13,6 @@ * and need to be refreshed, or when a packet was damaged in transit. * This may be have to be moved to the VFS layer. * - * NB: BSD uses a more intelligent approach to guessing when a request - * or reply has been lost by keeping the RTO estimate for each procedure. - * We currently make do with a constant timeout value. - * * Copyright (C) 1992,1993 Rick Sladkey <jrs@world.std.com> * Copyright (C) 1995,1996 Olaf Kirch <okir@monad.swb.de> */ @@ -32,7 +28,9 @@ #include <linux/slab.h> #include <linux/utsname.h> #include <linux/workqueue.h> +#include <linux/in.h> #include <linux/in6.h> +#include <linux/un.h> #include <linux/sunrpc/clnt.h> #include <linux/sunrpc/rpc_pipe_fs.h> @@ -298,22 +296,27 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args) * up a string representation of the passed-in address. */ if (args->servername == NULL) { + struct sockaddr_un *sun = + (struct sockaddr_un *)args->address; + struct sockaddr_in *sin = + (struct sockaddr_in *)args->address; + struct sockaddr_in6 *sin6 = + (struct sockaddr_in6 *)args->address; + servername[0] = '\0'; switch (args->address->sa_family) { - case AF_INET: { - struct sockaddr_in *sin = - (struct sockaddr_in *)args->address; + case AF_LOCAL: + snprintf(servername, sizeof(servername), "%s", + sun->sun_path); + break; + case AF_INET: snprintf(servername, sizeof(servername), "%pI4", &sin->sin_addr.s_addr); break; - } - case AF_INET6: { - struct sockaddr_in6 *sin = - (struct sockaddr_in6 *)args->address; + case AF_INET6: snprintf(servername, sizeof(servername), "%pI6", - &sin->sin6_addr); + &sin6->sin6_addr); break; - } default: /* caller wants default server name, but * address family isn't recognized. */ @@ -1508,7 +1511,10 @@ call_timeout(struct rpc_task *task) if (clnt->cl_chatty) printk(KERN_NOTICE "%s: server %s not responding, timed out\n", clnt->cl_protname, clnt->cl_server); - rpc_exit(task, -EIO); + if (task->tk_flags & RPC_TASK_TIMEOUT) + rpc_exit(task, -ETIMEDOUT); + else + rpc_exit(task, -EIO); return; } diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c index c652e4cc9fe..9a80a922c52 100644 --- a/net/sunrpc/rpcb_clnt.c +++ b/net/sunrpc/rpcb_clnt.c @@ -16,6 +16,7 @@ #include <linux/types.h> #include <linux/socket.h> +#include <linux/un.h> #include <linux/in.h> #include <linux/in6.h> #include <linux/kernel.h> @@ -32,6 +33,8 @@ # define RPCDBG_FACILITY RPCDBG_BIND #endif +#define RPCBIND_SOCK_PATHNAME "/var/run/rpcbind.sock" + #define RPCBIND_PROGRAM (100000u) #define RPCBIND_PORT (111u) @@ -158,20 +161,69 @@ static void rpcb_map_release(void *data) kfree(map); } -static const struct sockaddr_in rpcb_inaddr_loopback = { - .sin_family = AF_INET, - .sin_addr.s_addr = htonl(INADDR_LOOPBACK), - .sin_port = htons(RPCBIND_PORT), -}; +/* + * Returns zero on success, otherwise a negative errno value + * is returned. + */ +static int rpcb_create_local_unix(void) +{ + static const struct sockaddr_un rpcb_localaddr_rpcbind = { + .sun_family = AF_LOCAL, + .sun_path = RPCBIND_SOCK_PATHNAME, + }; + struct rpc_create_args args = { + .net = &init_net, + .protocol = XPRT_TRANSPORT_LOCAL, + .address = (struct sockaddr *)&rpcb_localaddr_rpcbind, + .addrsize = sizeof(rpcb_localaddr_rpcbind), + .servername = "localhost", + .program = &rpcb_program, + .version = RPCBVERS_2, + .authflavor = RPC_AUTH_NULL, + }; + struct rpc_clnt *clnt, *clnt4; + int result = 0; + + /* + * Because we requested an RPC PING at transport creation time, + * this works only if the user space portmapper is rpcbind, and + * it's listening on AF_LOCAL on the named socket. + */ + clnt = rpc_create(&args); + if (IS_ERR(clnt)) { + dprintk("RPC: failed to create AF_LOCAL rpcbind " + "client (errno %ld).\n", PTR_ERR(clnt)); + result = -PTR_ERR(clnt); + goto out; + } + + clnt4 = rpc_bind_new_program(clnt, &rpcb_program, RPCBVERS_4); + if (IS_ERR(clnt4)) { + dprintk("RPC: failed to bind second program to " + "rpcbind v4 client (errno %ld).\n", + PTR_ERR(clnt4)); + clnt4 = NULL; + } + + /* Protected by rpcb_create_local_mutex */ + rpcb_local_clnt = clnt; + rpcb_local_clnt4 = clnt4; -static DEFINE_MUTEX(rpcb_create_local_mutex); +out: + return result; +} /* * Returns zero on success, otherwise a negative errno value * is returned. */ -static int rpcb_create_local(void) +static int rpcb_create_local_net(void) { + static const struct sockaddr_in rpcb_inaddr_loopback = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(INADDR_LOOPBACK), + .sin_port = htons(RPCBIND_PORT), + }; struct rpc_create_args args = { .net = &init_net, .protocol = XPRT_TRANSPORT_TCP, @@ -186,13 +238,6 @@ static int rpcb_create_local(void) struct rpc_clnt *clnt, *clnt4; int result = 0; - if (rpcb_local_clnt) - return result; - - mutex_lock(&rpcb_create_local_mutex); - if (rpcb_local_clnt) - goto out; - clnt = rpc_create(&args); if (IS_ERR(clnt)) { dprintk("RPC: failed to create local rpcbind " @@ -214,10 +259,34 @@ static int rpcb_create_local(void) clnt4 = NULL; } + /* Protected by rpcb_create_local_mutex */ rpcb_local_clnt = clnt; rpcb_local_clnt4 = clnt4; out: + return result; +} + +/* + * Returns zero on success, otherwise a negative errno value + * is returned. + */ +static int rpcb_create_local(void) +{ + static DEFINE_MUTEX(rpcb_create_local_mutex); + int result = 0; + + if (rpcb_local_clnt) + return result; + + mutex_lock(&rpcb_create_local_mutex); + if (rpcb_local_clnt) + goto out; + + if (rpcb_create_local_unix() != 0) + result = rpcb_create_local_net(); + +out: mutex_unlock(&rpcb_create_local_mutex); return result; } diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 08e05a8ce02..2b90292e950 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -942,6 +942,8 @@ static void svc_unregister(const struct svc_serv *serv) if (progp->pg_vers[i]->vs_hidden) continue; + dprintk("svc: attempting to unregister %sv%u\n", + progp->pg_name, i); __svc_unregister(progp->pg_prog, i, progp->pg_name); } } diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index b7d435c3f19..af04f779ce9 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -387,6 +387,33 @@ static int svc_recvfrom(struct svc_rqst *rqstp, struct kvec *iov, int nr, return len; } +static int svc_partial_recvfrom(struct svc_rqst *rqstp, + struct kvec *iov, int nr, + int buflen, unsigned int base) +{ + size_t save_iovlen; + void __user *save_iovbase; + unsigned int i; + int ret; + + if (base == 0) + return svc_recvfrom(rqstp, iov, nr, buflen); + + for (i = 0; i < nr; i++) { + if (iov[i].iov_len > base) + break; + base -= iov[i].iov_len; + } + save_iovlen = iov[i].iov_len; + save_iovbase = iov[i].iov_base; + iov[i].iov_len -= base; + iov[i].iov_base += base; + ret = svc_recvfrom(rqstp, &iov[i], nr - i, buflen); + iov[i].iov_len = save_iovlen; + iov[i].iov_base = save_iovbase; + return ret; +} + /* * Set socket snd and rcv buffer lengths */ @@ -409,7 +436,6 @@ static void svc_sock_setbufsize(struct socket *sock, unsigned int snd, lock_sock(sock->sk); sock->sk->sk_sndbuf = snd * 2; sock->sk->sk_rcvbuf = rcv * 2; - sock->sk->sk_userlocks |= SOCK_SNDBUF_LOCK|SOCK_RCVBUF_LOCK; sock->sk->sk_write_space(sock->sk); release_sock(sock->sk); #endif @@ -884,6 +910,56 @@ failed: return NULL; } +static unsigned int svc_tcp_restore_pages(struct svc_sock *svsk, struct svc_rqst *rqstp) +{ + unsigned int i, len, npages; + + if (svsk->sk_tcplen <= sizeof(rpc_fraghdr)) + return 0; + len = svsk->sk_tcplen - sizeof(rpc_fraghdr); + npages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; + for (i = 0; i < npages; i++) { + if (rqstp->rq_pages[i] != NULL) + put_page(rqstp->rq_pages[i]); + BUG_ON(svsk->sk_pages[i] == NULL); + rqstp->rq_pages[i] = svsk->sk_pages[i]; + svsk->sk_pages[i] = NULL; + } + rqstp->rq_arg.head[0].iov_base = page_address(rqstp->rq_pages[0]); + return len; +} + +static void svc_tcp_save_pages(struct svc_sock *svsk, struct svc_rqst *rqstp) +{ + unsigned int i, len, npages; + + if (svsk->sk_tcplen <= sizeof(rpc_fraghdr)) + return; + len = svsk->sk_tcplen - sizeof(rpc_fraghdr); + npages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; + for (i = 0; i < npages; i++) { + svsk->sk_pages[i] = rqstp->rq_pages[i]; + rqstp->rq_pages[i] = NULL; + } +} + +static void svc_tcp_clear_pages(struct svc_sock *svsk) +{ + unsigned int i, len, npages; + + if (svsk->sk_tcplen <= sizeof(rpc_fraghdr)) + goto out; + len = svsk->sk_tcplen - sizeof(rpc_fraghdr); + npages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; + for (i = 0; i < npages; i++) { + BUG_ON(svsk->sk_pages[i] == NULL); + put_page(svsk->sk_pages[i]); + svsk->sk_pages[i] = NULL; + } +out: + svsk->sk_tcplen = 0; +} + /* * Receive data. * If we haven't gotten the record length yet, get the next four bytes. @@ -893,31 +969,15 @@ failed: static int svc_tcp_recv_record(struct svc_sock *svsk, struct svc_rqst *rqstp) { struct svc_serv *serv = svsk->sk_xprt.xpt_server; + unsigned int want; int len; - if (test_and_clear_bit(XPT_CHNGBUF, &svsk->sk_xprt.xpt_flags)) - /* sndbuf needs to have room for one request - * per thread, otherwise we can stall even when the - * network isn't a bottleneck. - * - * We count all threads rather than threads in a - * particular pool, which provides an upper bound - * on the number of threads which will access the socket. - * - * rcvbuf just needs to be able to hold a few requests. - * Normally they will be removed from the queue - * as soon a a complete request arrives. - */ - svc_sock_setbufsize(svsk->sk_sock, - (serv->sv_nrthreads+3) * serv->sv_max_mesg, - 3 * serv->sv_max_mesg); - clear_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags); if (svsk->sk_tcplen < sizeof(rpc_fraghdr)) { - int want = sizeof(rpc_fraghdr) - svsk->sk_tcplen; struct kvec iov; + want = sizeof(rpc_fraghdr) - svsk->sk_tcplen; iov.iov_base = ((char *) &svsk->sk_reclen) + svsk->sk_tcplen; iov.iov_len = want; if ((len = svc_recvfrom(rqstp, &iov, 1, want)) < 0) @@ -927,7 +987,7 @@ static int svc_tcp_recv_record(struct svc_sock *svsk, struct svc_rqst *rqstp) if (len < want) { dprintk("svc: short recvfrom while reading record " "length (%d of %d)\n", len, want); - goto err_again; /* record header not complete */ + return -EAGAIN; } svsk->sk_reclen = ntohl(svsk->sk_reclen); @@ -954,83 +1014,75 @@ static int svc_tcp_recv_record(struct svc_sock *svsk, struct svc_rqst *rqstp) } } - /* Check whether enough data is available */ - len = svc_recv_available(svsk); - if (len < 0) - goto error; + if (svsk->sk_reclen < 8) + goto err_delete; /* client is nuts. */ - if (len < svsk->sk_reclen) { - dprintk("svc: incomplete TCP record (%d of %d)\n", - len, svsk->sk_reclen); - goto err_again; /* record not complete */ - } len = svsk->sk_reclen; - set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags); return len; - error: - if (len == -EAGAIN) - dprintk("RPC: TCP recv_record got EAGAIN\n"); +error: + dprintk("RPC: TCP recv_record got %d\n", len); return len; - err_delete: +err_delete: set_bit(XPT_CLOSE, &svsk->sk_xprt.xpt_flags); - err_again: return -EAGAIN; } -static int svc_process_calldir(struct svc_sock *svsk, struct svc_rqst *rqstp, - struct rpc_rqst **reqpp, struct kvec *vec) +static int receive_cb_reply(struct svc_sock *svsk, struct svc_rqst *rqstp) { + struct rpc_xprt *bc_xprt = svsk->sk_xprt.xpt_bc_xprt; struct rpc_rqst *req = NULL; - u32 *p; - u32 xid; - u32 calldir; - int len; - - len = svc_recvfrom(rqstp, vec, 1, 8); - if (len < 0) - goto error; + struct kvec *src, *dst; + __be32 *p = (__be32 *)rqstp->rq_arg.head[0].iov_base; + __be32 xid; + __be32 calldir; - p = (u32 *)rqstp->rq_arg.head[0].iov_base; xid = *p++; calldir = *p; - if (calldir == 0) { - /* REQUEST is the most common case */ - vec[0] = rqstp->rq_arg.head[0]; - } else { - /* REPLY */ - struct rpc_xprt *bc_xprt = svsk->sk_xprt.xpt_bc_xprt; - - if (bc_xprt) - req = xprt_lookup_rqst(bc_xprt, xid); - - if (!req) { - printk(KERN_NOTICE - "%s: Got unrecognized reply: " - "calldir 0x%x xpt_bc_xprt %p xid %08x\n", - __func__, ntohl(calldir), - bc_xprt, xid); - vec[0] = rqstp->rq_arg.head[0]; - goto out; - } + if (bc_xprt) + req = xprt_lookup_rqst(bc_xprt, xid); - memcpy(&req->rq_private_buf, &req->rq_rcv_buf, - sizeof(struct xdr_buf)); - /* copy the xid and call direction */ - memcpy(req->rq_private_buf.head[0].iov_base, - rqstp->rq_arg.head[0].iov_base, 8); - vec[0] = req->rq_private_buf.head[0]; + if (!req) { + printk(KERN_NOTICE + "%s: Got unrecognized reply: " + "calldir 0x%x xpt_bc_xprt %p xid %08x\n", + __func__, ntohl(calldir), + bc_xprt, xid); + return -EAGAIN; } - out: - vec[0].iov_base += 8; - vec[0].iov_len -= 8; - len = svsk->sk_reclen - 8; - error: - *reqpp = req; - return len; + + memcpy(&req->rq_private_buf, &req->rq_rcv_buf, sizeof(struct xdr_buf)); + /* + * XXX!: cheating for now! Only copying HEAD. + * But we know this is good enough for now (in fact, for any + * callback reply in the forseeable future). + */ + dst = &req->rq_private_buf.head[0]; + src = &rqstp->rq_arg.head[0]; + if (dst->iov_len < src->iov_len) + return -EAGAIN; /* whatever; just giving up. */ + memcpy(dst->iov_base, src->iov_base, src->iov_len); + xprt_complete_rqst(req->rq_task, svsk->sk_reclen); + rqstp->rq_arg.len = 0; + return 0; } +static int copy_pages_to_kvecs(struct kvec *vec, struct page **pages, int len) +{ + int i = 0; + int t = 0; + + while (t < len) { + vec[i].iov_base = page_address(pages[i]); + vec[i].iov_len = PAGE_SIZE; + i++; + t += PAGE_SIZE; + } + return i; +} + + /* * Receive data from a TCP socket. */ @@ -1041,8 +1093,10 @@ static int svc_tcp_recvfrom(struct svc_rqst *rqstp) struct svc_serv *serv = svsk->sk_xprt.xpt_server; int len; struct kvec *vec; - int pnum, vlen; - struct rpc_rqst *req = NULL; + unsigned int want, base; + __be32 *p; + __be32 calldir; + int pnum; dprintk("svc: tcp_recv %p data %d conn %d close %d\n", svsk, test_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags), @@ -1053,87 +1107,73 @@ static int svc_tcp_recvfrom(struct svc_rqst *rqstp) if (len < 0) goto error; + base = svc_tcp_restore_pages(svsk, rqstp); + want = svsk->sk_reclen - base; + vec = rqstp->rq_vec; - vec[0] = rqstp->rq_arg.head[0]; - vlen = PAGE_SIZE; - /* - * We have enough data for the whole tcp record. Let's try and read the - * first 8 bytes to get the xid and the call direction. We can use this - * to figure out if this is a call or a reply to a callback. If - * sk_reclen is < 8 (xid and calldir), then this is a malformed packet. - * In that case, don't bother with the calldir and just read the data. - * It will be rejected in svc_process. - */ - if (len >= 8) { - len = svc_process_calldir(svsk, rqstp, &req, vec); - if (len < 0) - goto err_again; - vlen -= 8; - } + pnum = copy_pages_to_kvecs(&vec[0], &rqstp->rq_pages[0], + svsk->sk_reclen); - pnum = 1; - while (vlen < len) { - vec[pnum].iov_base = (req) ? - page_address(req->rq_private_buf.pages[pnum - 1]) : - page_address(rqstp->rq_pages[pnum]); - vec[pnum].iov_len = PAGE_SIZE; - pnum++; - vlen += PAGE_SIZE; - } rqstp->rq_respages = &rqstp->rq_pages[pnum]; /* Now receive data */ - len = svc_recvfrom(rqstp, vec, pnum, len); - if (len < 0) - goto err_again; - - /* - * Account for the 8 bytes we read earlier - */ - len += 8; - - if (req) { - xprt_complete_rqst(req->rq_task, len); - len = 0; - goto out; + len = svc_partial_recvfrom(rqstp, vec, pnum, want, base); + if (len >= 0) + svsk->sk_tcplen += len; + if (len != want) { + if (len < 0 && len != -EAGAIN) + goto err_other; + svc_tcp_save_pages(svsk, rqstp); + dprintk("svc: incomplete TCP record (%d of %d)\n", + svsk->sk_tcplen, svsk->sk_reclen); + goto err_noclose; } - dprintk("svc: TCP complete record (%d bytes)\n", len); - rqstp->rq_arg.len = len; + + rqstp->rq_arg.len = svsk->sk_reclen; rqstp->rq_arg.page_base = 0; - if (len <= rqstp->rq_arg.head[0].iov_len) { - rqstp->rq_arg.head[0].iov_len = len; + if (rqstp->rq_arg.len <= rqstp->rq_arg.head[0].iov_len) { + rqstp->rq_arg.head[0].iov_len = rqstp->rq_arg.len; rqstp->rq_arg.page_len = 0; - } else { - rqstp->rq_arg.page_len = len - rqstp->rq_arg.head[0].iov_len; - } + } else + rqstp->rq_arg.page_len = rqstp->rq_arg.len - rqstp->rq_arg.head[0].iov_len; rqstp->rq_xprt_ctxt = NULL; rqstp->rq_prot = IPPROTO_TCP; -out: + p = (__be32 *)rqstp->rq_arg.head[0].iov_base; + calldir = p[1]; + if (calldir) + len = receive_cb_reply(svsk, rqstp); + /* Reset TCP read info */ svsk->sk_reclen = 0; svsk->sk_tcplen = 0; + /* If we have more data, signal svc_xprt_enqueue() to try again */ + if (svc_recv_available(svsk) > sizeof(rpc_fraghdr)) + set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags); + + if (len < 0) + goto error; svc_xprt_copy_addrs(rqstp, &svsk->sk_xprt); if (serv->sv_stats) serv->sv_stats->nettcpcnt++; - return len; + dprintk("svc: TCP complete record (%d bytes)\n", rqstp->rq_arg.len); + return rqstp->rq_arg.len; -err_again: - if (len == -EAGAIN) { - dprintk("RPC: TCP recvfrom got EAGAIN\n"); - return len; - } error: - if (len != -EAGAIN) { - printk(KERN_NOTICE "%s: recvfrom returned errno %d\n", - svsk->sk_xprt.xpt_server->sv_name, -len); - set_bit(XPT_CLOSE, &svsk->sk_xprt.xpt_flags); - } + if (len != -EAGAIN) + goto err_other; + dprintk("RPC: TCP recvfrom got EAGAIN\n"); return -EAGAIN; +err_other: + printk(KERN_NOTICE "%s: recvfrom returned errno %d\n", + svsk->sk_xprt.xpt_server->sv_name, -len); + set_bit(XPT_CLOSE, &svsk->sk_xprt.xpt_flags); +err_noclose: + return -EAGAIN; /* record not complete */ } /* @@ -1304,18 +1344,10 @@ static void svc_tcp_init(struct svc_sock *svsk, struct svc_serv *serv) svsk->sk_reclen = 0; svsk->sk_tcplen = 0; + memset(&svsk->sk_pages[0], 0, sizeof(svsk->sk_pages)); tcp_sk(sk)->nonagle |= TCP_NAGLE_OFF; - /* initialise setting must have enough space to - * receive and respond to one request. - * svc_tcp_recvfrom will re-adjust if necessary - */ - svc_sock_setbufsize(svsk->sk_sock, - 3 * svsk->sk_xprt.xpt_server->sv_max_mesg, - 3 * svsk->sk_xprt.xpt_server->sv_max_mesg); - - set_bit(XPT_CHNGBUF, &svsk->sk_xprt.xpt_flags); set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags); if (sk->sk_state != TCP_ESTABLISHED) set_bit(XPT_CLOSE, &svsk->sk_xprt.xpt_flags); @@ -1379,8 +1411,14 @@ static struct svc_sock *svc_setup_socket(struct svc_serv *serv, /* Initialize the socket */ if (sock->type == SOCK_DGRAM) svc_udp_init(svsk, serv); - else + else { + /* initialise setting must have enough space to + * receive and respond to one request. + */ + svc_sock_setbufsize(svsk->sk_sock, 4 * serv->sv_max_mesg, + 4 * serv->sv_max_mesg); svc_tcp_init(svsk, serv); + } dprintk("svc: svc_setup_socket created %p (inet %p)\n", svsk, svsk->sk_sk); @@ -1562,8 +1600,10 @@ static void svc_tcp_sock_detach(struct svc_xprt *xprt) svc_sock_detach(xprt); - if (!test_bit(XPT_LISTENER, &xprt->xpt_flags)) + if (!test_bit(XPT_LISTENER, &xprt->xpt_flags)) { + svc_tcp_clear_pages(svsk); kernel_sock_shutdown(svsk->sk_sock, SHUT_RDWR); + } } /* diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 679cd674b81..f008c14ad34 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -638,6 +638,25 @@ void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p) } EXPORT_SYMBOL_GPL(xdr_init_decode); +/** + * xdr_init_decode - Initialize an xdr_stream for decoding data. + * @xdr: pointer to xdr_stream struct + * @buf: pointer to XDR buffer from which to decode data + * @pages: list of pages to decode into + * @len: length in bytes of buffer in pages + */ +void xdr_init_decode_pages(struct xdr_stream *xdr, struct xdr_buf *buf, + struct page **pages, unsigned int len) +{ + memset(buf, 0, sizeof(*buf)); + buf->pages = pages; + buf->page_len = len; + buf->buflen = len; + buf->len = len; + xdr_init_decode(xdr, buf, NULL); +} +EXPORT_SYMBOL_GPL(xdr_init_decode_pages); + static __be32 * __xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes) { __be32 *p = xdr->p; diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 9494c376735..ce5eb68a966 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -906,6 +906,7 @@ void xprt_transmit(struct rpc_task *task) } dprintk("RPC: %5u xmit complete\n", task->tk_pid); + task->tk_flags |= RPC_TASK_SENT; spin_lock_bh(&xprt->transport_lock); xprt->ops->set_retrans_timeout(task); diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index 1a10dcd999e..a385430c722 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -42,6 +42,7 @@ #include <linux/sunrpc/svc_xprt.h> #include <linux/sunrpc/debug.h> #include <linux/sunrpc/rpc_rdma.h> +#include <linux/interrupt.h> #include <linux/sched.h> #include <linux/slab.h> #include <linux/spinlock.h> @@ -333,7 +334,7 @@ static void rq_cq_reap(struct svcxprt_rdma *xprt) } /* - * Processs a completion context + * Process a completion context */ static void process_context(struct svcxprt_rdma *xprt, struct svc_rdma_op_ctxt *ctxt) @@ -695,7 +696,8 @@ static struct svc_xprt *svc_rdma_create(struct svc_serv *serv, return ERR_PTR(-ENOMEM); xprt = &cma_xprt->sc_xprt; - listen_id = rdma_create_id(rdma_listen_handler, cma_xprt, RDMA_PS_TCP); + listen_id = rdma_create_id(rdma_listen_handler, cma_xprt, RDMA_PS_TCP, + IB_QPT_RC); if (IS_ERR(listen_id)) { ret = PTR_ERR(listen_id); dprintk("svcrdma: rdma_create_id failed = %d\n", ret); diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index d4297dc43dc..28236bab57f 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -47,6 +47,7 @@ * o buffer memory */ +#include <linux/interrupt.h> #include <linux/pci.h> /* for Tavor hack below */ #include <linux/slab.h> @@ -387,7 +388,7 @@ rpcrdma_create_id(struct rpcrdma_xprt *xprt, init_completion(&ia->ri_done); - id = rdma_create_id(rpcrdma_conn_upcall, xprt, RDMA_PS_TCP); + id = rdma_create_id(rpcrdma_conn_upcall, xprt, RDMA_PS_TCP, IB_QPT_RC); if (IS_ERR(id)) { rc = PTR_ERR(id); dprintk("RPC: %s: rdma_create_id() failed %i\n", diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index bf005d3c65e..72abb735893 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -19,6 +19,7 @@ */ #include <linux/types.h> +#include <linux/string.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/capability.h> @@ -28,6 +29,7 @@ #include <linux/in.h> #include <linux/net.h> #include <linux/mm.h> +#include <linux/un.h> #include <linux/udp.h> #include <linux/tcp.h> #include <linux/sunrpc/clnt.h> @@ -45,6 +47,9 @@ #include <net/tcp.h> #include "sunrpc.h" + +static void xs_close(struct rpc_xprt *xprt); + /* * xprtsock tunables */ @@ -261,6 +266,11 @@ static inline struct sockaddr *xs_addr(struct rpc_xprt *xprt) return (struct sockaddr *) &xprt->addr; } +static inline struct sockaddr_un *xs_addr_un(struct rpc_xprt *xprt) +{ + return (struct sockaddr_un *) &xprt->addr; +} + static inline struct sockaddr_in *xs_addr_in(struct rpc_xprt *xprt) { return (struct sockaddr_in *) &xprt->addr; @@ -276,23 +286,34 @@ static void xs_format_common_peer_addresses(struct rpc_xprt *xprt) struct sockaddr *sap = xs_addr(xprt); struct sockaddr_in6 *sin6; struct sockaddr_in *sin; + struct sockaddr_un *sun; char buf[128]; - (void)rpc_ntop(sap, buf, sizeof(buf)); - xprt->address_strings[RPC_DISPLAY_ADDR] = kstrdup(buf, GFP_KERNEL); - switch (sap->sa_family) { + case AF_LOCAL: + sun = xs_addr_un(xprt); + strlcpy(buf, sun->sun_path, sizeof(buf)); + xprt->address_strings[RPC_DISPLAY_ADDR] = + kstrdup(buf, GFP_KERNEL); + break; case AF_INET: + (void)rpc_ntop(sap, buf, sizeof(buf)); + xprt->address_strings[RPC_DISPLAY_ADDR] = + kstrdup(buf, GFP_KERNEL); sin = xs_addr_in(xprt); snprintf(buf, sizeof(buf), "%08x", ntohl(sin->sin_addr.s_addr)); break; case AF_INET6: + (void)rpc_ntop(sap, buf, sizeof(buf)); + xprt->address_strings[RPC_DISPLAY_ADDR] = + kstrdup(buf, GFP_KERNEL); sin6 = xs_addr_in6(xprt); snprintf(buf, sizeof(buf), "%pi6", &sin6->sin6_addr); break; default: BUG(); } + xprt->address_strings[RPC_DISPLAY_HEX_ADDR] = kstrdup(buf, GFP_KERNEL); } @@ -495,6 +516,70 @@ static int xs_nospace(struct rpc_task *task) return ret; } +/* + * Construct a stream transport record marker in @buf. + */ +static inline void xs_encode_stream_record_marker(struct xdr_buf *buf) +{ + u32 reclen = buf->len - sizeof(rpc_fraghdr); + rpc_fraghdr *base = buf->head[0].iov_base; + *base = cpu_to_be32(RPC_LAST_STREAM_FRAGMENT | reclen); +} + +/** + * xs_local_send_request - write an RPC request to an AF_LOCAL socket + * @task: RPC task that manages the state of an RPC request + * + * Return values: + * 0: The request has been sent + * EAGAIN: The socket was blocked, please call again later to + * complete the request + * ENOTCONN: Caller needs to invoke connect logic then call again + * other: Some other error occured, the request was not sent + */ +static int xs_local_send_request(struct rpc_task *task) +{ + struct rpc_rqst *req = task->tk_rqstp; + struct rpc_xprt *xprt = req->rq_xprt; + struct sock_xprt *transport = + container_of(xprt, struct sock_xprt, xprt); + struct xdr_buf *xdr = &req->rq_snd_buf; + int status; + + xs_encode_stream_record_marker(&req->rq_snd_buf); + + xs_pktdump("packet data:", + req->rq_svec->iov_base, req->rq_svec->iov_len); + + status = xs_sendpages(transport->sock, NULL, 0, + xdr, req->rq_bytes_sent); + dprintk("RPC: %s(%u) = %d\n", + __func__, xdr->len - req->rq_bytes_sent, status); + if (likely(status >= 0)) { + req->rq_bytes_sent += status; + req->rq_xmit_bytes_sent += status; + if (likely(req->rq_bytes_sent >= req->rq_slen)) { + req->rq_bytes_sent = 0; + return 0; + } + status = -EAGAIN; + } + + switch (status) { + case -EAGAIN: + status = xs_nospace(task); + break; + default: + dprintk("RPC: sendmsg returned unrecognized error %d\n", + -status); + case -EPIPE: + xs_close(xprt); + status = -ENOTCONN; + } + + return status; +} + /** * xs_udp_send_request - write an RPC request to a UDP socket * @task: address of RPC task that manages the state of an RPC request @@ -574,13 +659,6 @@ static void xs_tcp_shutdown(struct rpc_xprt *xprt) kernel_sock_shutdown(sock, SHUT_WR); } -static inline void xs_encode_tcp_record_marker(struct xdr_buf *buf) -{ - u32 reclen = buf->len - sizeof(rpc_fraghdr); - rpc_fraghdr *base = buf->head[0].iov_base; - *base = htonl(RPC_LAST_STREAM_FRAGMENT | reclen); -} - /** * xs_tcp_send_request - write an RPC request to a TCP socket * @task: address of RPC task that manages the state of an RPC request @@ -603,7 +681,7 @@ static int xs_tcp_send_request(struct rpc_task *task) struct xdr_buf *xdr = &req->rq_snd_buf; int status; - xs_encode_tcp_record_marker(&req->rq_snd_buf); + xs_encode_stream_record_marker(&req->rq_snd_buf); xs_pktdump("packet data:", req->rq_svec->iov_base, @@ -785,6 +863,88 @@ static inline struct rpc_xprt *xprt_from_sock(struct sock *sk) return (struct rpc_xprt *) sk->sk_user_data; } +static int xs_local_copy_to_xdr(struct xdr_buf *xdr, struct sk_buff *skb) +{ + struct xdr_skb_reader desc = { + .skb = skb, + .offset = sizeof(rpc_fraghdr), + .count = skb->len - sizeof(rpc_fraghdr), + }; + + if (xdr_partial_copy_from_skb(xdr, 0, &desc, xdr_skb_read_bits) < 0) + return -1; + if (desc.count) + return -1; + return 0; +} + +/** + * xs_local_data_ready - "data ready" callback for AF_LOCAL sockets + * @sk: socket with data to read + * @len: how much data to read + * + * Currently this assumes we can read the whole reply in a single gulp. + */ +static void xs_local_data_ready(struct sock *sk, int len) +{ + struct rpc_task *task; + struct rpc_xprt *xprt; + struct rpc_rqst *rovr; + struct sk_buff *skb; + int err, repsize, copied; + u32 _xid; + __be32 *xp; + + read_lock_bh(&sk->sk_callback_lock); + dprintk("RPC: %s...\n", __func__); + xprt = xprt_from_sock(sk); + if (xprt == NULL) + goto out; + + skb = skb_recv_datagram(sk, 0, 1, &err); + if (skb == NULL) + goto out; + + if (xprt->shutdown) + goto dropit; + + repsize = skb->len - sizeof(rpc_fraghdr); + if (repsize < 4) { + dprintk("RPC: impossible RPC reply size %d\n", repsize); + goto dropit; + } + + /* Copy the XID from the skb... */ + xp = skb_header_pointer(skb, sizeof(rpc_fraghdr), sizeof(_xid), &_xid); + if (xp == NULL) + goto dropit; + + /* Look up and lock the request corresponding to the given XID */ + spin_lock(&xprt->transport_lock); + rovr = xprt_lookup_rqst(xprt, *xp); + if (!rovr) + goto out_unlock; + task = rovr->rq_task; + + copied = rovr->rq_private_buf.buflen; + if (copied > repsize) + copied = repsize; + + if (xs_local_copy_to_xdr(&rovr->rq_private_buf, skb)) { + dprintk("RPC: sk_buff copy failed\n"); + goto out_unlock; + } + + xprt_complete_rqst(task, copied); + + out_unlock: + spin_unlock(&xprt->transport_lock); + dropit: + skb_free_datagram(sk, skb); + out: + read_unlock_bh(&sk->sk_callback_lock); +} + /** * xs_udp_data_ready - "data ready" callback for UDP sockets * @sk: socket with data to read @@ -1344,7 +1504,6 @@ static void xs_tcp_state_change(struct sock *sk) case TCP_CLOSE_WAIT: /* The server initiated a shutdown of the socket */ xprt_force_disconnect(xprt); - case TCP_SYN_SENT: xprt->connect_cookie++; case TCP_CLOSING: /* @@ -1571,11 +1730,31 @@ static int xs_bind(struct sock_xprt *transport, struct socket *sock) return err; } +/* + * We don't support autobind on AF_LOCAL sockets + */ +static void xs_local_rpcbind(struct rpc_task *task) +{ + xprt_set_bound(task->tk_xprt); +} + +static void xs_local_set_port(struct rpc_xprt *xprt, unsigned short port) +{ +} #ifdef CONFIG_DEBUG_LOCK_ALLOC static struct lock_class_key xs_key[2]; static struct lock_class_key xs_slock_key[2]; +static inline void xs_reclassify_socketu(struct socket *sock) +{ + struct sock *sk = sock->sk; + + BUG_ON(sock_owned_by_user(sk)); + sock_lock_init_class_and_name(sk, "slock-AF_LOCAL-RPC", + &xs_slock_key[1], "sk_lock-AF_LOCAL-RPC", &xs_key[1]); +} + static inline void xs_reclassify_socket4(struct socket *sock) { struct sock *sk = sock->sk; @@ -1597,6 +1776,9 @@ static inline void xs_reclassify_socket6(struct socket *sock) static inline void xs_reclassify_socket(int family, struct socket *sock) { switch (family) { + case AF_LOCAL: + xs_reclassify_socketu(sock); + break; case AF_INET: xs_reclassify_socket4(sock); break; @@ -1606,6 +1788,10 @@ static inline void xs_reclassify_socket(int family, struct socket *sock) } } #else +static inline void xs_reclassify_socketu(struct socket *sock) +{ +} + static inline void xs_reclassify_socket4(struct socket *sock) { } @@ -1644,6 +1830,94 @@ out: return ERR_PTR(err); } +static int xs_local_finish_connecting(struct rpc_xprt *xprt, + struct socket *sock) +{ + struct sock_xprt *transport = container_of(xprt, struct sock_xprt, + xprt); + + if (!transport->inet) { + struct sock *sk = sock->sk; + + write_lock_bh(&sk->sk_callback_lock); + + xs_save_old_callbacks(transport, sk); + + sk->sk_user_data = xprt; + sk->sk_data_ready = xs_local_data_ready; + sk->sk_write_space = xs_udp_write_space; + sk->sk_error_report = xs_error_report; + sk->sk_allocation = GFP_ATOMIC; + + xprt_clear_connected(xprt); + + /* Reset to new socket */ + transport->sock = sock; + transport->inet = sk; + + write_unlock_bh(&sk->sk_callback_lock); + } + + /* Tell the socket layer to start connecting... */ + xprt->stat.connect_count++; + xprt->stat.connect_start = jiffies; + return kernel_connect(sock, xs_addr(xprt), xprt->addrlen, 0); +} + +/** + * xs_local_setup_socket - create AF_LOCAL socket, connect to a local endpoint + * @xprt: RPC transport to connect + * @transport: socket transport to connect + * @create_sock: function to create a socket of the correct type + * + * Invoked by a work queue tasklet. + */ +static void xs_local_setup_socket(struct work_struct *work) +{ + struct sock_xprt *transport = + container_of(work, struct sock_xprt, connect_worker.work); + struct rpc_xprt *xprt = &transport->xprt; + struct socket *sock; + int status = -EIO; + + if (xprt->shutdown) + goto out; + + clear_bit(XPRT_CONNECTION_ABORT, &xprt->state); + status = __sock_create(xprt->xprt_net, AF_LOCAL, + SOCK_STREAM, 0, &sock, 1); + if (status < 0) { + dprintk("RPC: can't create AF_LOCAL " + "transport socket (%d).\n", -status); + goto out; + } + xs_reclassify_socketu(sock); + + dprintk("RPC: worker connecting xprt %p via AF_LOCAL to %s\n", + xprt, xprt->address_strings[RPC_DISPLAY_ADDR]); + + status = xs_local_finish_connecting(xprt, sock); + switch (status) { + case 0: + dprintk("RPC: xprt %p connected to %s\n", + xprt, xprt->address_strings[RPC_DISPLAY_ADDR]); + xprt_set_connected(xprt); + break; + case -ENOENT: + dprintk("RPC: xprt %p: socket %s does not exist\n", + xprt, xprt->address_strings[RPC_DISPLAY_ADDR]); + break; + default: + printk(KERN_ERR "%s: unhandled error (%d) connecting to %s\n", + __func__, -status, + xprt->address_strings[RPC_DISPLAY_ADDR]); + } + +out: + xprt_clear_connecting(xprt); + xprt_wake_pending_tasks(xprt, status); +} + static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) { struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt); @@ -1758,6 +2032,7 @@ static void xs_tcp_reuse_connection(struct sock_xprt *transport) static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) { struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt); + int ret = -ENOTCONN; if (!transport->inet) { struct sock *sk = sock->sk; @@ -1789,12 +2064,22 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) } if (!xprt_bound(xprt)) - return -ENOTCONN; + goto out; /* Tell the socket layer to start connecting... */ xprt->stat.connect_count++; xprt->stat.connect_start = jiffies; - return kernel_connect(sock, xs_addr(xprt), xprt->addrlen, O_NONBLOCK); + ret = kernel_connect(sock, xs_addr(xprt), xprt->addrlen, O_NONBLOCK); + switch (ret) { + case 0: + case -EINPROGRESS: + /* SYN_SENT! */ + xprt->connect_cookie++; + if (xprt->reestablish_timeout < XS_TCP_INIT_REEST_TO) + xprt->reestablish_timeout = XS_TCP_INIT_REEST_TO; + } +out: + return ret; } /** @@ -1917,6 +2202,32 @@ static void xs_connect(struct rpc_task *task) } /** + * xs_local_print_stats - display AF_LOCAL socket-specifc stats + * @xprt: rpc_xprt struct containing statistics + * @seq: output file + * + */ +static void xs_local_print_stats(struct rpc_xprt *xprt, struct seq_file *seq) +{ + long idle_time = 0; + + if (xprt_connected(xprt)) + idle_time = (long)(jiffies - xprt->last_used) / HZ; + + seq_printf(seq, "\txprt:\tlocal %lu %lu %lu %ld %lu %lu %lu " + "%llu %llu\n", + xprt->stat.bind_count, + xprt->stat.connect_count, + xprt->stat.connect_time, + idle_time, + xprt->stat.sends, + xprt->stat.recvs, + xprt->stat.bad_xids, + xprt->stat.req_u, + xprt->stat.bklog_u); +} + +/** * xs_udp_print_stats - display UDP socket-specifc stats * @xprt: rpc_xprt struct containing statistics * @seq: output file @@ -2014,10 +2325,7 @@ static int bc_sendto(struct rpc_rqst *req) unsigned long headoff; unsigned long tailoff; - /* - * Set up the rpc header and record marker stuff - */ - xs_encode_tcp_record_marker(xbufp); + xs_encode_stream_record_marker(xbufp); tailoff = (unsigned long)xbufp->tail[0].iov_base & ~PAGE_MASK; headoff = (unsigned long)xbufp->head[0].iov_base & ~PAGE_MASK; @@ -2089,6 +2397,21 @@ static void bc_destroy(struct rpc_xprt *xprt) { } +static struct rpc_xprt_ops xs_local_ops = { + .reserve_xprt = xprt_reserve_xprt, + .release_xprt = xs_tcp_release_xprt, + .rpcbind = xs_local_rpcbind, + .set_port = xs_local_set_port, + .connect = xs_connect, + .buf_alloc = rpc_malloc, + .buf_free = rpc_free, + .send_request = xs_local_send_request, + .set_retrans_timeout = xprt_set_retrans_timeout_def, + .close = xs_close, + .destroy = xs_destroy, + .print_stats = xs_local_print_stats, +}; + static struct rpc_xprt_ops xs_udp_ops = { .set_buffer_size = xs_udp_set_buffer_size, .reserve_xprt = xprt_reserve_xprt_cong, @@ -2150,6 +2473,8 @@ static int xs_init_anyaddr(const int family, struct sockaddr *sap) }; switch (family) { + case AF_LOCAL: + break; case AF_INET: memcpy(sap, &sin, sizeof(sin)); break; @@ -2197,6 +2522,70 @@ static struct rpc_xprt *xs_setup_xprt(struct xprt_create *args, return xprt; } +static const struct rpc_timeout xs_local_default_timeout = { + .to_initval = 10 * HZ, + .to_maxval = 10 * HZ, + .to_retries = 2, +}; + +/** + * xs_setup_local - Set up transport to use an AF_LOCAL socket + * @args: rpc transport creation arguments + * + * AF_LOCAL is a "tpi_cots_ord" transport, just like TCP + */ +static struct rpc_xprt *xs_setup_local(struct xprt_create *args) +{ + struct sockaddr_un *sun = (struct sockaddr_un *)args->dstaddr; + struct sock_xprt *transport; + struct rpc_xprt *xprt; + struct rpc_xprt *ret; + + xprt = xs_setup_xprt(args, xprt_tcp_slot_table_entries); + if (IS_ERR(xprt)) + return xprt; + transport = container_of(xprt, struct sock_xprt, xprt); + + xprt->prot = 0; + xprt->tsh_size = sizeof(rpc_fraghdr) / sizeof(u32); + xprt->max_payload = RPC_MAX_FRAGMENT_SIZE; + + xprt->bind_timeout = XS_BIND_TO; + xprt->reestablish_timeout = XS_TCP_INIT_REEST_TO; + xprt->idle_timeout = XS_IDLE_DISC_TO; + + xprt->ops = &xs_local_ops; + xprt->timeout = &xs_local_default_timeout; + + switch (sun->sun_family) { + case AF_LOCAL: + if (sun->sun_path[0] != '/') { + dprintk("RPC: bad AF_LOCAL address: %s\n", + sun->sun_path); + ret = ERR_PTR(-EINVAL); + goto out_err; + } + xprt_set_bound(xprt); + INIT_DELAYED_WORK(&transport->connect_worker, + xs_local_setup_socket); + xs_format_peer_addresses(xprt, "local", RPCBIND_NETID_LOCAL); + break; + default: + ret = ERR_PTR(-EAFNOSUPPORT); + goto out_err; + } + + dprintk("RPC: set up xprt to %s via AF_LOCAL\n", + xprt->address_strings[RPC_DISPLAY_ADDR]); + + if (try_module_get(THIS_MODULE)) + return xprt; + ret = ERR_PTR(-EINVAL); +out_err: + xprt_free(xprt); + return ret; +} + static const struct rpc_timeout xs_udp_default_timeout = { .to_initval = 5 * HZ, .to_maxval = 30 * HZ, @@ -2438,6 +2827,14 @@ out_err: return ret; } +static struct xprt_class xs_local_transport = { + .list = LIST_HEAD_INIT(xs_local_transport.list), + .name = "named UNIX socket", + .owner = THIS_MODULE, + .ident = XPRT_TRANSPORT_LOCAL, + .setup = xs_setup_local, +}; + static struct xprt_class xs_udp_transport = { .list = LIST_HEAD_INIT(xs_udp_transport.list), .name = "udp", @@ -2473,6 +2870,7 @@ int init_socket_xprt(void) sunrpc_table_header = register_sysctl_table(sunrpc_table); #endif + xprt_register_transport(&xs_local_transport); xprt_register_transport(&xs_udp_transport); xprt_register_transport(&xs_tcp_transport); xprt_register_transport(&xs_bc_tcp_transport); @@ -2493,6 +2891,7 @@ void cleanup_socket_xprt(void) } #endif + xprt_unregister_transport(&xs_local_transport); xprt_unregister_transport(&xs_udp_transport); xprt_unregister_transport(&xs_tcp_transport); xprt_unregister_transport(&xs_bc_tcp_transport); diff --git a/net/tipc/addr.h b/net/tipc/addr.h index 8971aba99ae..e4f35afe320 100644 --- a/net/tipc/addr.h +++ b/net/tipc/addr.h @@ -37,14 +37,17 @@ #ifndef _TIPC_ADDR_H #define _TIPC_ADDR_H +#define TIPC_ZONE_MASK 0xff000000u +#define TIPC_CLUSTER_MASK 0xfffff000u + static inline u32 tipc_zone_mask(u32 addr) { - return addr & 0xff000000u; + return addr & TIPC_ZONE_MASK; } static inline u32 tipc_cluster_mask(u32 addr) { - return addr & 0xfffff000u; + return addr & TIPC_CLUSTER_MASK; } static inline int in_own_cluster(u32 addr) diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c index 7dc1dc7151e..fa68d1e9ff4 100644 --- a/net/tipc/bcast.c +++ b/net/tipc/bcast.c @@ -44,13 +44,6 @@ #define BCLINK_WIN_DEFAULT 20 /* bcast link window size (default) */ -/* - * Loss rate for incoming broadcast frames; used to test retransmission code. - * Set to N to cause every N'th frame to be discarded; 0 => don't discard any. - */ - -#define TIPC_BCAST_LOSS_RATE 0 - /** * struct bcbearer_pair - a pair of bearers used by broadcast link * @primary: pointer to primary bearer @@ -414,9 +407,7 @@ int tipc_bclink_send_msg(struct sk_buff *buf) spin_lock_bh(&bc_lock); res = tipc_link_send_buf(bcl, buf); - if (unlikely(res == -ELINKCONG)) - buf_discard(buf); - else + if (likely(res > 0)) bclink_set_last_sent(); bcl->stats.queue_sz_counts++; @@ -434,9 +425,6 @@ int tipc_bclink_send_msg(struct sk_buff *buf) void tipc_bclink_recv_pkt(struct sk_buff *buf) { -#if (TIPC_BCAST_LOSS_RATE) - static int rx_count; -#endif struct tipc_msg *msg = buf_msg(buf); struct tipc_node *node = tipc_node_find(msg_prevnode(msg)); u32 next_in; @@ -470,14 +458,6 @@ void tipc_bclink_recv_pkt(struct sk_buff *buf) return; } -#if (TIPC_BCAST_LOSS_RATE) - if (++rx_count == TIPC_BCAST_LOSS_RATE) { - rx_count = 0; - buf_discard(buf); - return; - } -#endif - tipc_node_lock(node); receive: deferred = node->bclink.deferred_head; diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index 411719feb80..85209eadfae 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -46,6 +46,8 @@ static u32 media_count; struct tipc_bearer tipc_bearers[MAX_BEARERS]; +static void bearer_disable(struct tipc_bearer *b_ptr); + /** * media_name_valid - validate media name * @@ -342,15 +344,15 @@ struct sk_buff *tipc_bearer_get_names(void) void tipc_bearer_add_dest(struct tipc_bearer *b_ptr, u32 dest) { tipc_nmap_add(&b_ptr->nodes, dest); - tipc_disc_update_link_req(b_ptr->link_req); tipc_bcbearer_sort(); + tipc_disc_add_dest(b_ptr->link_req); } void tipc_bearer_remove_dest(struct tipc_bearer *b_ptr, u32 dest) { tipc_nmap_remove(&b_ptr->nodes, dest); - tipc_disc_update_link_req(b_ptr->link_req); tipc_bcbearer_sort(); + tipc_disc_remove_dest(b_ptr->link_req); } /* @@ -493,8 +495,15 @@ int tipc_enable_bearer(const char *name, u32 disc_domain, u32 priority) warn("Bearer <%s> rejected, illegal name\n", name); return -EINVAL; } - if (!tipc_addr_domain_valid(disc_domain) || - !tipc_in_scope(disc_domain, tipc_own_addr)) { + if (tipc_addr_domain_valid(disc_domain) && + (disc_domain != tipc_own_addr)) { + if (tipc_in_scope(disc_domain, tipc_own_addr)) { + disc_domain = tipc_own_addr & TIPC_CLUSTER_MASK; + res = 0; /* accept any node in own cluster */ + } else if (in_own_cluster(disc_domain)) + res = 0; /* accept specified node in own cluster */ + } + if (res) { warn("Bearer <%s> rejected, illegal discovery domain\n", name); return -EINVAL; } @@ -511,7 +520,7 @@ int tipc_enable_bearer(const char *name, u32 disc_domain, u32 priority) if (!m_ptr) { warn("Bearer <%s> rejected, media <%s> not registered\n", name, b_name.media_name); - goto failed; + goto exit; } if (priority == TIPC_MEDIA_LINK_PRI) @@ -527,14 +536,14 @@ restart: } if (!strcmp(name, tipc_bearers[i].name)) { warn("Bearer <%s> rejected, already enabled\n", name); - goto failed; + goto exit; } if ((tipc_bearers[i].priority == priority) && (++with_this_prio > 2)) { if (priority-- == 0) { warn("Bearer <%s> rejected, duplicate priority\n", name); - goto failed; + goto exit; } warn("Bearer <%s> priority adjustment required %u->%u\n", name, priority + 1, priority); @@ -544,7 +553,7 @@ restart: if (bearer_id >= MAX_BEARERS) { warn("Bearer <%s> rejected, bearer limit reached (%u)\n", name, MAX_BEARERS); - goto failed; + goto exit; } b_ptr = &tipc_bearers[bearer_id]; @@ -552,7 +561,7 @@ restart: res = m_ptr->enable_bearer(b_ptr); if (res) { warn("Bearer <%s> rejected, enable failure (%d)\n", name, -res); - goto failed; + goto exit; } b_ptr->identity = bearer_id; @@ -562,14 +571,18 @@ restart: b_ptr->priority = priority; INIT_LIST_HEAD(&b_ptr->cong_links); INIT_LIST_HEAD(&b_ptr->links); - b_ptr->link_req = tipc_disc_init_link_req(b_ptr, &m_ptr->bcast_addr, - disc_domain); spin_lock_init(&b_ptr->lock); - write_unlock_bh(&tipc_net_lock); + + res = tipc_disc_create(b_ptr, &m_ptr->bcast_addr, disc_domain); + if (res) { + bearer_disable(b_ptr); + warn("Bearer <%s> rejected, discovery object creation failed\n", + name); + goto exit; + } info("Enabled bearer <%s>, discovery domain %s, priority %u\n", name, tipc_addr_string_fill(addr_string, disc_domain), priority); - return 0; -failed: +exit: write_unlock_bh(&tipc_net_lock); return res; } @@ -620,14 +633,14 @@ static void bearer_disable(struct tipc_bearer *b_ptr) struct link *temp_l_ptr; info("Disabling bearer <%s>\n", b_ptr->name); - tipc_disc_stop_link_req(b_ptr->link_req); spin_lock_bh(&b_ptr->lock); - b_ptr->link_req = NULL; b_ptr->blocked = 1; b_ptr->media->disable_bearer(b_ptr); list_for_each_entry_safe(l_ptr, temp_l_ptr, &b_ptr->links, link_list) { tipc_link_delete(l_ptr); } + if (b_ptr->link_req) + tipc_disc_delete(b_ptr->link_req); spin_unlock_bh(&b_ptr->lock); memset(b_ptr, 0, sizeof(struct tipc_bearer)); } diff --git a/net/tipc/core.c b/net/tipc/core.c index c9a73e7763f..943b6af8426 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -179,8 +179,7 @@ static int __init tipc_init(void) if (tipc_log_resize(CONFIG_TIPC_LOG) != 0) warn("Unable to create log buffer\n"); - info("Activated (version " TIPC_MOD_VER - " compiled " __DATE__ " " __TIME__ ")\n"); + info("Activated (version " TIPC_MOD_VER ")\n"); tipc_own_addr = 0; tipc_remote_management = 1; diff --git a/net/tipc/discover.c b/net/tipc/discover.c index 491eff56b9d..0987933155b 100644 --- a/net/tipc/discover.c +++ b/net/tipc/discover.c @@ -39,19 +39,17 @@ #include "discover.h" #define TIPC_LINK_REQ_INIT 125 /* min delay during bearer start up */ -#define TIPC_LINK_REQ_FAST 2000 /* normal delay if bearer has no links */ -#define TIPC_LINK_REQ_SLOW 600000 /* normal delay if bearer has links */ - -/* - * TODO: Most of the inter-cluster setup stuff should be - * rewritten, and be made conformant with specification. - */ +#define TIPC_LINK_REQ_FAST 1000 /* max delay if bearer has no links */ +#define TIPC_LINK_REQ_SLOW 60000 /* max delay if bearer has links */ +#define TIPC_LINK_REQ_INACTIVE 0xffffffff /* indicates no timer in use */ /** * struct link_req - information about an ongoing link setup request * @bearer: bearer issuing requests * @dest: destination address for request messages + * @domain: network domain to which links can be established + * @num_nodes: number of nodes currently discovered (i.e. with an active link) * @buf: request message to be (repeatedly) sent * @timer: timer governing period between requests * @timer_intv: current interval between requests (in ms) @@ -59,6 +57,8 @@ struct link_req { struct tipc_bearer *bearer; struct tipc_media_addr dest; + u32 domain; + int num_nodes; struct sk_buff *buf; struct timer_list timer; unsigned int timer_intv; @@ -147,7 +147,7 @@ void tipc_disc_recv_msg(struct sk_buff *buf, struct tipc_bearer *b_ptr) } if (!tipc_in_scope(dest, tipc_own_addr)) return; - if (!in_own_cluster(orig)) + if (!tipc_in_scope(b_ptr->link_req->domain, orig)) return; /* Locate structure corresponding to requesting node */ @@ -214,44 +214,54 @@ void tipc_disc_recv_msg(struct sk_buff *buf, struct tipc_bearer *b_ptr) } /** - * tipc_disc_stop_link_req - stop sending periodic link setup requests + * disc_update - update frequency of periodic link setup requests * @req: ptr to link request structure + * + * Reinitiates discovery process if discovery object has no associated nodes + * and is either not currently searching or is searching at a slow rate */ -void tipc_disc_stop_link_req(struct link_req *req) +static void disc_update(struct link_req *req) { - if (!req) - return; + if (!req->num_nodes) { + if ((req->timer_intv == TIPC_LINK_REQ_INACTIVE) || + (req->timer_intv > TIPC_LINK_REQ_FAST)) { + req->timer_intv = TIPC_LINK_REQ_INIT; + k_start_timer(&req->timer, req->timer_intv); + } + } +} - k_cancel_timer(&req->timer); - k_term_timer(&req->timer); - buf_discard(req->buf); - kfree(req); +/** + * tipc_disc_add_dest - increment set of discovered nodes + * @req: ptr to link request structure + */ + +void tipc_disc_add_dest(struct link_req *req) +{ + req->num_nodes++; } /** - * tipc_disc_update_link_req - update frequency of periodic link setup requests + * tipc_disc_remove_dest - decrement set of discovered nodes * @req: ptr to link request structure */ -void tipc_disc_update_link_req(struct link_req *req) +void tipc_disc_remove_dest(struct link_req *req) { - if (!req) - return; + req->num_nodes--; + disc_update(req); +} - if (req->timer_intv == TIPC_LINK_REQ_SLOW) { - if (!req->bearer->nodes.count) { - req->timer_intv = TIPC_LINK_REQ_FAST; - k_start_timer(&req->timer, req->timer_intv); - } - } else if (req->timer_intv == TIPC_LINK_REQ_FAST) { - if (req->bearer->nodes.count) { - req->timer_intv = TIPC_LINK_REQ_SLOW; - k_start_timer(&req->timer, req->timer_intv); - } - } else { - /* leave timer "as is" if haven't yet reached a "normal" rate */ - } +/** + * disc_send_msg - send link setup request message + * @req: ptr to link request structure + */ + +static void disc_send_msg(struct link_req *req) +{ + if (!req->bearer->blocked) + tipc_bearer_send(req->bearer, req->buf, &req->dest); } /** @@ -263,56 +273,86 @@ void tipc_disc_update_link_req(struct link_req *req) static void disc_timeout(struct link_req *req) { + int max_delay; + spin_lock_bh(&req->bearer->lock); - req->bearer->media->send_msg(req->buf, req->bearer, &req->dest); - - if ((req->timer_intv == TIPC_LINK_REQ_SLOW) || - (req->timer_intv == TIPC_LINK_REQ_FAST)) { - /* leave timer interval "as is" if already at a "normal" rate */ - } else { - req->timer_intv *= 2; - if (req->timer_intv > TIPC_LINK_REQ_FAST) - req->timer_intv = TIPC_LINK_REQ_FAST; - if ((req->timer_intv == TIPC_LINK_REQ_FAST) && - (req->bearer->nodes.count)) - req->timer_intv = TIPC_LINK_REQ_SLOW; + /* Stop searching if only desired node has been found */ + + if (tipc_node(req->domain) && req->num_nodes) { + req->timer_intv = TIPC_LINK_REQ_INACTIVE; + goto exit; } - k_start_timer(&req->timer, req->timer_intv); + /* + * Send discovery message, then update discovery timer + * + * Keep doubling time between requests until limit is reached; + * hold at fast polling rate if don't have any associated nodes, + * otherwise hold at slow polling rate + */ + + disc_send_msg(req); + + req->timer_intv *= 2; + if (req->num_nodes) + max_delay = TIPC_LINK_REQ_SLOW; + else + max_delay = TIPC_LINK_REQ_FAST; + if (req->timer_intv > max_delay) + req->timer_intv = max_delay; + + k_start_timer(&req->timer, req->timer_intv); +exit: spin_unlock_bh(&req->bearer->lock); } /** - * tipc_disc_init_link_req - start sending periodic link setup requests + * tipc_disc_create - create object to send periodic link setup requests * @b_ptr: ptr to bearer issuing requests * @dest: destination address for request messages - * @dest_domain: network domain of node(s) which should respond to message + * @dest_domain: network domain to which links can be established * - * Returns pointer to link request structure, or NULL if unable to create. + * Returns 0 if successful, otherwise -errno. */ -struct link_req *tipc_disc_init_link_req(struct tipc_bearer *b_ptr, - const struct tipc_media_addr *dest, - u32 dest_domain) +int tipc_disc_create(struct tipc_bearer *b_ptr, + struct tipc_media_addr *dest, u32 dest_domain) { struct link_req *req; req = kmalloc(sizeof(*req), GFP_ATOMIC); if (!req) - return NULL; + return -ENOMEM; req->buf = tipc_disc_init_msg(DSC_REQ_MSG, dest_domain, b_ptr); if (!req->buf) { kfree(req); - return NULL; + return -ENOMSG; } memcpy(&req->dest, dest, sizeof(*dest)); req->bearer = b_ptr; + req->domain = dest_domain; + req->num_nodes = 0; req->timer_intv = TIPC_LINK_REQ_INIT; k_init_timer(&req->timer, (Handler)disc_timeout, (unsigned long)req); k_start_timer(&req->timer, req->timer_intv); - return req; + b_ptr->link_req = req; + disc_send_msg(req); + return 0; +} + +/** + * tipc_disc_delete - destroy object sending periodic link setup requests + * @req: ptr to link request structure + */ + +void tipc_disc_delete(struct link_req *req) +{ + k_cancel_timer(&req->timer); + k_term_timer(&req->timer); + buf_discard(req->buf); + kfree(req); } diff --git a/net/tipc/discover.h b/net/tipc/discover.h index e48a167e47b..a3af595b86c 100644 --- a/net/tipc/discover.h +++ b/net/tipc/discover.h @@ -39,12 +39,11 @@ struct link_req; -struct link_req *tipc_disc_init_link_req(struct tipc_bearer *b_ptr, - const struct tipc_media_addr *dest, - u32 dest_domain); -void tipc_disc_update_link_req(struct link_req *req); -void tipc_disc_stop_link_req(struct link_req *req); - +int tipc_disc_create(struct tipc_bearer *b_ptr, struct tipc_media_addr *dest, + u32 dest_domain); +void tipc_disc_delete(struct link_req *req); +void tipc_disc_add_dest(struct link_req *req); +void tipc_disc_remove_dest(struct link_req *req); void tipc_disc_recv_msg(struct sk_buff *buf, struct tipc_bearer *b_ptr); #endif diff --git a/net/tipc/link.c b/net/tipc/link.c index ebf338f7b14..5ed4b4f7452 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -92,7 +92,8 @@ static int link_recv_changeover_msg(struct link **l_ptr, struct sk_buff **buf); static void link_set_supervision_props(struct link *l_ptr, u32 tolerance); static int link_send_sections_long(struct tipc_port *sender, struct iovec const *msg_sect, - u32 num_sect, u32 destnode); + u32 num_sect, unsigned int total_len, + u32 destnode); static void link_check_defragm_bufs(struct link *l_ptr); static void link_state_event(struct link *l_ptr, u32 event); static void link_reset_statistics(struct link *l_ptr); @@ -842,6 +843,25 @@ static void link_add_to_outqueue(struct link *l_ptr, l_ptr->stats.max_queue_sz = l_ptr->out_queue_size; } +static void link_add_chain_to_outqueue(struct link *l_ptr, + struct sk_buff *buf_chain, + u32 long_msgno) +{ + struct sk_buff *buf; + struct tipc_msg *msg; + + if (!l_ptr->next_out) + l_ptr->next_out = buf_chain; + while (buf_chain) { + buf = buf_chain; + buf_chain = buf_chain->next; + + msg = buf_msg(buf); + msg_set_long_msgno(msg, long_msgno); + link_add_to_outqueue(l_ptr, buf, msg); + } +} + /* * tipc_link_send_buf() is the 'full path' for messages, called from * inside TIPC when the 'fast path' in tipc_send_buf @@ -864,8 +884,9 @@ int tipc_link_send_buf(struct link *l_ptr, struct sk_buff *buf) if (unlikely(queue_size >= queue_limit)) { if (imp <= TIPC_CRITICAL_IMPORTANCE) { - return link_schedule_port(l_ptr, msg_origport(msg), - size); + link_schedule_port(l_ptr, msg_origport(msg), size); + buf_discard(buf); + return -ELINKCONG; } buf_discard(buf); if (imp > CONN_MANAGER) { @@ -1042,6 +1063,7 @@ int tipc_send_buf_fast(struct sk_buff *buf, u32 destnode) int tipc_link_send_sections_fast(struct tipc_port *sender, struct iovec const *msg_sect, const u32 num_sect, + unsigned int total_len, u32 destaddr) { struct tipc_msg *hdr = &sender->phdr; @@ -1057,8 +1079,8 @@ again: * (Must not hold any locks while building message.) */ - res = tipc_msg_build(hdr, msg_sect, num_sect, sender->max_pkt, - !sender->user_port, &buf); + res = tipc_msg_build(hdr, msg_sect, num_sect, total_len, + sender->max_pkt, !sender->user_port, &buf); read_lock_bh(&tipc_net_lock); node = tipc_node_find(destaddr); @@ -1069,8 +1091,6 @@ again: if (likely(buf)) { res = link_send_buf_fast(l_ptr, buf, &sender->max_pkt); - if (unlikely(res < 0)) - buf_discard(buf); exit: tipc_node_unlock(node); read_unlock_bh(&tipc_net_lock); @@ -1105,7 +1125,8 @@ exit: goto again; return link_send_sections_long(sender, msg_sect, - num_sect, destaddr); + num_sect, total_len, + destaddr); } tipc_node_unlock(node); } @@ -1117,7 +1138,7 @@ exit: return tipc_reject_msg(buf, TIPC_ERR_NO_NODE); if (res >= 0) return tipc_port_reject_sections(sender, hdr, msg_sect, num_sect, - TIPC_ERR_NO_NODE); + total_len, TIPC_ERR_NO_NODE); return res; } @@ -1138,12 +1159,13 @@ exit: static int link_send_sections_long(struct tipc_port *sender, struct iovec const *msg_sect, u32 num_sect, + unsigned int total_len, u32 destaddr) { struct link *l_ptr; struct tipc_node *node; struct tipc_msg *hdr = &sender->phdr; - u32 dsz = msg_data_sz(hdr); + u32 dsz = total_len; u32 max_pkt, fragm_sz, rest; struct tipc_msg fragm_hdr; struct sk_buff *buf, *buf_chain, *prev; @@ -1169,7 +1191,6 @@ again: tipc_msg_init(&fragm_hdr, MSG_FRAGMENTER, FIRST_FRAGMENT, INT_H_SIZE, msg_destnode(hdr)); - msg_set_link_selector(&fragm_hdr, sender->ref); msg_set_size(&fragm_hdr, max_pkt); msg_set_fragm_no(&fragm_hdr, 1); @@ -1271,28 +1292,15 @@ reject: buf_discard(buf_chain); } return tipc_port_reject_sections(sender, hdr, msg_sect, num_sect, - TIPC_ERR_NO_NODE); + total_len, TIPC_ERR_NO_NODE); } - /* Append whole chain to send queue: */ + /* Append chain of fragments to send queue & send them */ - buf = buf_chain; - l_ptr->long_msg_seq_no = mod(l_ptr->long_msg_seq_no + 1); - if (!l_ptr->next_out) - l_ptr->next_out = buf_chain; + l_ptr->long_msg_seq_no++; + link_add_chain_to_outqueue(l_ptr, buf_chain, l_ptr->long_msg_seq_no); + l_ptr->stats.sent_fragments += fragm_no; l_ptr->stats.sent_fragmented++; - while (buf) { - struct sk_buff *next = buf->next; - struct tipc_msg *msg = buf_msg(buf); - - l_ptr->stats.sent_fragments++; - msg_set_long_msgno(msg, l_ptr->long_msg_seq_no); - link_add_to_outqueue(l_ptr, buf, msg); - buf = next; - } - - /* Send it, if possible: */ - tipc_link_push_queue(l_ptr); tipc_node_unlock(node); return dsz; @@ -2407,6 +2415,8 @@ void tipc_link_recv_bundle(struct sk_buff *buf) */ static int link_send_long_buf(struct link *l_ptr, struct sk_buff *buf) { + struct sk_buff *buf_chain = NULL; + struct sk_buff *buf_chain_tail = (struct sk_buff *)&buf_chain; struct tipc_msg *inmsg = buf_msg(buf); struct tipc_msg fragm_hdr; u32 insize = msg_size(inmsg); @@ -2415,7 +2425,7 @@ static int link_send_long_buf(struct link *l_ptr, struct sk_buff *buf) u32 rest = insize; u32 pack_sz = l_ptr->max_pkt; u32 fragm_sz = pack_sz - INT_H_SIZE; - u32 fragm_no = 1; + u32 fragm_no = 0; u32 destaddr; if (msg_short(inmsg)) @@ -2427,10 +2437,6 @@ static int link_send_long_buf(struct link *l_ptr, struct sk_buff *buf) tipc_msg_init(&fragm_hdr, MSG_FRAGMENTER, FIRST_FRAGMENT, INT_H_SIZE, destaddr); - msg_set_link_selector(&fragm_hdr, msg_link_selector(inmsg)); - msg_set_long_msgno(&fragm_hdr, mod(l_ptr->long_msg_seq_no++)); - msg_set_fragm_no(&fragm_hdr, fragm_no); - l_ptr->stats.sent_fragmented++; /* Chop up message: */ @@ -2443,27 +2449,37 @@ static int link_send_long_buf(struct link *l_ptr, struct sk_buff *buf) } fragm = tipc_buf_acquire(fragm_sz + INT_H_SIZE); if (fragm == NULL) { - warn("Link unable to fragment message\n"); - dsz = -ENOMEM; - goto exit; + buf_discard(buf); + while (buf_chain) { + buf = buf_chain; + buf_chain = buf_chain->next; + buf_discard(buf); + } + return -ENOMEM; } msg_set_size(&fragm_hdr, fragm_sz + INT_H_SIZE); + fragm_no++; + msg_set_fragm_no(&fragm_hdr, fragm_no); skb_copy_to_linear_data(fragm, &fragm_hdr, INT_H_SIZE); skb_copy_to_linear_data_offset(fragm, INT_H_SIZE, crs, fragm_sz); - /* Send queued messages first, if any: */ + buf_chain_tail->next = fragm; + buf_chain_tail = fragm; - l_ptr->stats.sent_fragments++; - tipc_link_send_buf(l_ptr, fragm); - if (!tipc_link_is_up(l_ptr)) - return dsz; - msg_set_fragm_no(&fragm_hdr, ++fragm_no); rest -= fragm_sz; crs += fragm_sz; msg_set_type(&fragm_hdr, FRAGMENT); } -exit: buf_discard(buf); + + /* Append chain of fragments to send queue & send them */ + + l_ptr->long_msg_seq_no++; + link_add_chain_to_outqueue(l_ptr, buf_chain, l_ptr->long_msg_seq_no); + l_ptr->stats.sent_fragments += fragm_no; + l_ptr->stats.sent_fragmented++; + tipc_link_push_queue(l_ptr); + return dsz; } diff --git a/net/tipc/link.h b/net/tipc/link.h index e6a30dbe1aa..74fbecab1ea 100644 --- a/net/tipc/link.h +++ b/net/tipc/link.h @@ -228,6 +228,7 @@ u32 tipc_link_get_max_pkt(u32 dest, u32 selector); int tipc_link_send_sections_fast(struct tipc_port *sender, struct iovec const *msg_sect, const u32 num_sect, + unsigned int total_len, u32 destnode); void tipc_link_recv_bundle(struct sk_buff *buf); int tipc_link_recv_fragment(struct sk_buff **pending, diff --git a/net/tipc/msg.c b/net/tipc/msg.c index 6d92d17e7fb..03e57bf92c7 100644 --- a/net/tipc/msg.c +++ b/net/tipc/msg.c @@ -68,20 +68,6 @@ void tipc_msg_init(struct tipc_msg *m, u32 user, u32 type, } /** - * tipc_msg_calc_data_size - determine total data size for message - */ - -int tipc_msg_calc_data_size(struct iovec const *msg_sect, u32 num_sect) -{ - int dsz = 0; - int i; - - for (i = 0; i < num_sect; i++) - dsz += msg_sect[i].iov_len; - return dsz; -} - -/** * tipc_msg_build - create message using specified header and data * * Note: Caller must not hold any locks in case copy_from_user() is interrupted! @@ -89,18 +75,13 @@ int tipc_msg_calc_data_size(struct iovec const *msg_sect, u32 num_sect) * Returns message data size or errno */ -int tipc_msg_build(struct tipc_msg *hdr, - struct iovec const *msg_sect, u32 num_sect, +int tipc_msg_build(struct tipc_msg *hdr, struct iovec const *msg_sect, + u32 num_sect, unsigned int total_len, int max_size, int usrmem, struct sk_buff **buf) { int dsz, sz, hsz, pos, res, cnt; - dsz = tipc_msg_calc_data_size(msg_sect, num_sect); - if (unlikely(dsz > TIPC_MAX_USER_MSG_SIZE)) { - *buf = NULL; - return -EINVAL; - } - + dsz = total_len; pos = hsz = msg_hdr_sz(hdr); sz = hsz + dsz; msg_set_size(hdr, sz); diff --git a/net/tipc/msg.h b/net/tipc/msg.h index de02339fc17..8452454731f 100644 --- a/net/tipc/msg.h +++ b/net/tipc/msg.h @@ -39,41 +39,24 @@ #include "bearer.h" +/* + * Constants and routines used to read and write TIPC payload message headers + * + * Note: Some items are also used with TIPC internal message headers + */ + #define TIPC_VERSION 2 /* - * TIPC user data message header format, version 2: - * - * - * 1 0 9 8 7 6 5 4|3 2 1 0 9 8 7 6|5 4 3 2 1 0 9 8|7 6 5 4 3 2 1 0 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * w0:|vers | user |hdr sz |n|d|s|-| message size | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * w1:|mstyp| error |rer cnt|lsc|opt p| broadcast ack no | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * w2:| link level ack no | broadcast/link level seq no | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * w3:| previous node | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * w4:| originating port | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * w5:| destination port | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * w6:| originating node | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * w7:| destination node | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * w8:| name type / transport sequence number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * w9:| name instance/multicast lower bound | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * wA:| multicast upper bound | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * / / - * \ options \ - * / / - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * + * Payload message users are defined in TIPC's public API: + * - TIPC_LOW_IMPORTANCE + * - TIPC_MEDIUM_IMPORTANCE + * - TIPC_HIGH_IMPORTANCE + * - TIPC_CRITICAL_IMPORTANCE + */ + +/* + * Payload message types */ #define TIPC_CONN_MSG 0 @@ -81,6 +64,9 @@ #define TIPC_NAMED_MSG 2 #define TIPC_DIRECT_MSG 3 +/* + * Message header sizes + */ #define SHORT_H_SIZE 24 /* Connected, in-cluster messages */ #define DIR_MSG_H_SIZE 32 /* Directly addressed messages */ @@ -473,40 +459,11 @@ static inline struct tipc_msg *msg_get_wrapped(struct tipc_msg *m) /* - TIPC internal message header format, version 2 - - 1 0 9 8 7 6 5 4|3 2 1 0 9 8 7 6|5 4 3 2 1 0 9 8|7 6 5 4 3 2 1 0 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - w0:|vers |msg usr|hdr sz |n|resrv| packet size | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - w1:|m typ| sequence gap | broadcast ack no | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - w2:| link level ack no/bc_gap_from | seq no / bcast_gap_to | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - w3:| previous node | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - w4:| next sent broadcast/fragm no | next sent pkt/ fragm msg no | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - w5:| session no |rsv=0|r|berid|link prio|netpl|p| - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - w6:| originating node | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - w7:| destination node | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - w8:| transport sequence number | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - w9:| msg count / bcast tag | link tolerance | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - \ \ - / User Specific Data / - \ \ - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - NB: CONN_MANAGER use data message format. LINK_CONFIG has own format. -*/ + * Constants and routines used to read and write TIPC internal message headers + */ /* - * Internal users + * Internal message users */ #define BCAST_PROTOCOL 5 @@ -520,7 +477,7 @@ static inline struct tipc_msg *msg_get_wrapped(struct tipc_msg *m) #define LINK_CONFIG 13 /* - * Connection management protocol messages + * Connection management protocol message types */ #define CONN_PROBE 0 @@ -528,12 +485,41 @@ static inline struct tipc_msg *msg_get_wrapped(struct tipc_msg *m) #define CONN_ACK 2 /* - * Name distributor messages + * Name distributor message types */ #define PUBLICATION 0 #define WITHDRAWAL 1 +/* + * Segmentation message types + */ + +#define FIRST_FRAGMENT 0 +#define FRAGMENT 1 +#define LAST_FRAGMENT 2 + +/* + * Link management protocol message types + */ + +#define STATE_MSG 0 +#define RESET_MSG 1 +#define ACTIVATE_MSG 2 + +/* + * Changeover tunnel message types + */ +#define DUPLICATE_MSG 0 +#define ORIGINAL_MSG 1 + +/* + * Config protocol message types + */ + +#define DSC_REQ_MSG 0 +#define DSC_RESP_MSG 1 + /* * Word 1 @@ -761,50 +747,11 @@ static inline void msg_set_link_tolerance(struct tipc_msg *m, u32 n) msg_set_bits(m, 9, 0, 0xffff, n); } -/* - * Segmentation message types - */ - -#define FIRST_FRAGMENT 0 -#define FRAGMENT 1 -#define LAST_FRAGMENT 2 - -/* - * Link management protocol message types - */ - -#define STATE_MSG 0 -#define RESET_MSG 1 -#define ACTIVATE_MSG 2 - -/* - * Changeover tunnel message types - */ -#define DUPLICATE_MSG 0 -#define ORIGINAL_MSG 1 - -/* - * Routing table message types - */ -#define EXT_ROUTING_TABLE 0 -#define LOCAL_ROUTING_TABLE 1 /* obsoleted */ -#define SLAVE_ROUTING_TABLE 2 -#define ROUTE_ADDITION 3 -#define ROUTE_REMOVAL 4 - -/* - * Config protocol message types - */ - -#define DSC_REQ_MSG 0 -#define DSC_RESP_MSG 1 - u32 tipc_msg_tot_importance(struct tipc_msg *m); void tipc_msg_init(struct tipc_msg *m, u32 user, u32 type, u32 hsize, u32 destnode); -int tipc_msg_calc_data_size(struct iovec const *msg_sect, u32 num_sect); -int tipc_msg_build(struct tipc_msg *hdr, - struct iovec const *msg_sect, u32 num_sect, +int tipc_msg_build(struct tipc_msg *hdr, struct iovec const *msg_sect, + u32 num_sect, unsigned int total_len, int max_size, int usrmem, struct sk_buff **buf); static inline void msg_set_media_addr(struct tipc_msg *m, struct tipc_media_addr *a) diff --git a/net/tipc/port.c b/net/tipc/port.c index 6ff78f9c7d6..c68dc956a42 100644 --- a/net/tipc/port.c +++ b/net/tipc/port.c @@ -74,7 +74,8 @@ static u32 port_peerport(struct tipc_port *p_ptr) */ int tipc_multicast(u32 ref, struct tipc_name_seq const *seq, - u32 num_sect, struct iovec const *msg_sect) + u32 num_sect, struct iovec const *msg_sect, + unsigned int total_len) { struct tipc_msg *hdr; struct sk_buff *buf; @@ -91,11 +92,14 @@ int tipc_multicast(u32 ref, struct tipc_name_seq const *seq, hdr = &oport->phdr; msg_set_type(hdr, TIPC_MCAST_MSG); + msg_set_lookup_scope(hdr, TIPC_CLUSTER_SCOPE); + msg_set_destport(hdr, 0); + msg_set_destnode(hdr, 0); msg_set_nametype(hdr, seq->type); msg_set_namelower(hdr, seq->lower); msg_set_nameupper(hdr, seq->upper); msg_set_hdr_sz(hdr, MCAST_H_SIZE); - res = tipc_msg_build(hdr, msg_sect, num_sect, MAX_MSG_SIZE, + res = tipc_msg_build(hdr, msg_sect, num_sect, total_len, MAX_MSG_SIZE, !oport->user_port, &buf); if (unlikely(!buf)) return res; @@ -161,6 +165,7 @@ void tipc_port_recv_mcast(struct sk_buff *buf, struct port_list *dp) /* Deliver a copy of message to each destination port */ if (dp->count != 0) { + msg_set_destnode(msg, tipc_own_addr); if (dp->count == 1) { msg_set_destport(msg, dp->ports[0]); tipc_port_recv_msg(buf); @@ -414,12 +419,12 @@ int tipc_reject_msg(struct sk_buff *buf, u32 err) int tipc_port_reject_sections(struct tipc_port *p_ptr, struct tipc_msg *hdr, struct iovec const *msg_sect, u32 num_sect, - int err) + unsigned int total_len, int err) { struct sk_buff *buf; int res; - res = tipc_msg_build(hdr, msg_sect, num_sect, MAX_MSG_SIZE, + res = tipc_msg_build(hdr, msg_sect, num_sect, total_len, MAX_MSG_SIZE, !p_ptr->user_port, &buf); if (!buf) return res; @@ -1065,6 +1070,7 @@ int tipc_connect2port(u32 ref, struct tipc_portid const *peer) msg_set_orignode(msg, tipc_own_addr); msg_set_origport(msg, p_ptr->ref); msg_set_type(msg, TIPC_CONN_MSG); + msg_set_lookup_scope(msg, 0); msg_set_hdr_sz(msg, SHORT_H_SIZE); p_ptr->probing_interval = PROBING_INTERVAL; @@ -1158,12 +1164,13 @@ int tipc_shutdown(u32 ref) */ static int tipc_port_recv_sections(struct tipc_port *sender, unsigned int num_sect, - struct iovec const *msg_sect) + struct iovec const *msg_sect, + unsigned int total_len) { struct sk_buff *buf; int res; - res = tipc_msg_build(&sender->phdr, msg_sect, num_sect, + res = tipc_msg_build(&sender->phdr, msg_sect, num_sect, total_len, MAX_MSG_SIZE, !sender->user_port, &buf); if (likely(buf)) tipc_port_recv_msg(buf); @@ -1174,7 +1181,8 @@ static int tipc_port_recv_sections(struct tipc_port *sender, unsigned int num_se * tipc_send - send message sections on connection */ -int tipc_send(u32 ref, unsigned int num_sect, struct iovec const *msg_sect) +int tipc_send(u32 ref, unsigned int num_sect, struct iovec const *msg_sect, + unsigned int total_len) { struct tipc_port *p_ptr; u32 destnode; @@ -1189,9 +1197,10 @@ int tipc_send(u32 ref, unsigned int num_sect, struct iovec const *msg_sect) destnode = port_peernode(p_ptr); if (likely(destnode != tipc_own_addr)) res = tipc_link_send_sections_fast(p_ptr, msg_sect, num_sect, - destnode); + total_len, destnode); else - res = tipc_port_recv_sections(p_ptr, num_sect, msg_sect); + res = tipc_port_recv_sections(p_ptr, num_sect, msg_sect, + total_len); if (likely(res != -ELINKCONG)) { p_ptr->congested = 0; @@ -1202,8 +1211,7 @@ int tipc_send(u32 ref, unsigned int num_sect, struct iovec const *msg_sect) } if (port_unreliable(p_ptr)) { p_ptr->congested = 0; - /* Just calculate msg length and return */ - return tipc_msg_calc_data_size(msg_sect, num_sect); + return total_len; } return -ELINKCONG; } @@ -1213,7 +1221,8 @@ int tipc_send(u32 ref, unsigned int num_sect, struct iovec const *msg_sect) */ int tipc_send2name(u32 ref, struct tipc_name const *name, unsigned int domain, - unsigned int num_sect, struct iovec const *msg_sect) + unsigned int num_sect, struct iovec const *msg_sect, + unsigned int total_len) { struct tipc_port *p_ptr; struct tipc_msg *msg; @@ -1240,23 +1249,23 @@ int tipc_send2name(u32 ref, struct tipc_name const *name, unsigned int domain, if (likely(destport)) { if (likely(destnode == tipc_own_addr)) res = tipc_port_recv_sections(p_ptr, num_sect, - msg_sect); + msg_sect, total_len); else res = tipc_link_send_sections_fast(p_ptr, msg_sect, - num_sect, destnode); + num_sect, total_len, + destnode); if (likely(res != -ELINKCONG)) { if (res > 0) p_ptr->sent++; return res; } if (port_unreliable(p_ptr)) { - /* Just calculate msg length and return */ - return tipc_msg_calc_data_size(msg_sect, num_sect); + return total_len; } return -ELINKCONG; } return tipc_port_reject_sections(p_ptr, msg, msg_sect, num_sect, - TIPC_ERR_NO_NAME); + total_len, TIPC_ERR_NO_NAME); } /** @@ -1264,7 +1273,8 @@ int tipc_send2name(u32 ref, struct tipc_name const *name, unsigned int domain, */ int tipc_send2port(u32 ref, struct tipc_portid const *dest, - unsigned int num_sect, struct iovec const *msg_sect) + unsigned int num_sect, struct iovec const *msg_sect, + unsigned int total_len) { struct tipc_port *p_ptr; struct tipc_msg *msg; @@ -1276,6 +1286,7 @@ int tipc_send2port(u32 ref, struct tipc_portid const *dest, msg = &p_ptr->phdr; msg_set_type(msg, TIPC_DIRECT_MSG); + msg_set_lookup_scope(msg, 0); msg_set_orignode(msg, tipc_own_addr); msg_set_origport(msg, ref); msg_set_destnode(msg, dest->node); @@ -1283,18 +1294,18 @@ int tipc_send2port(u32 ref, struct tipc_portid const *dest, msg_set_hdr_sz(msg, DIR_MSG_H_SIZE); if (dest->node == tipc_own_addr) - res = tipc_port_recv_sections(p_ptr, num_sect, msg_sect); + res = tipc_port_recv_sections(p_ptr, num_sect, msg_sect, + total_len); else res = tipc_link_send_sections_fast(p_ptr, msg_sect, num_sect, - dest->node); + total_len, dest->node); if (likely(res != -ELINKCONG)) { if (res > 0) p_ptr->sent++; return res; } if (port_unreliable(p_ptr)) { - /* Just calculate msg length and return */ - return tipc_msg_calc_data_size(msg_sect, num_sect); + return total_len; } return -ELINKCONG; } diff --git a/net/tipc/port.h b/net/tipc/port.h index 87b9424ae0e..b9aa34195ae 100644 --- a/net/tipc/port.h +++ b/net/tipc/port.h @@ -205,23 +205,27 @@ int tipc_disconnect_port(struct tipc_port *tp_ptr); /* * TIPC messaging routines */ -int tipc_send(u32 portref, unsigned int num_sect, struct iovec const *msg_sect); +int tipc_send(u32 portref, unsigned int num_sect, struct iovec const *msg_sect, + unsigned int total_len); int tipc_send2name(u32 portref, struct tipc_name const *name, u32 domain, - unsigned int num_sect, struct iovec const *msg_sect); + unsigned int num_sect, struct iovec const *msg_sect, + unsigned int total_len); int tipc_send2port(u32 portref, struct tipc_portid const *dest, - unsigned int num_sect, struct iovec const *msg_sect); + unsigned int num_sect, struct iovec const *msg_sect, + unsigned int total_len); int tipc_send_buf2port(u32 portref, struct tipc_portid const *dest, struct sk_buff *buf, unsigned int dsz); int tipc_multicast(u32 portref, struct tipc_name_seq const *seq, - unsigned int section_count, struct iovec const *msg); + unsigned int section_count, struct iovec const *msg, + unsigned int total_len); int tipc_port_reject_sections(struct tipc_port *p_ptr, struct tipc_msg *hdr, struct iovec const *msg_sect, u32 num_sect, - int err); + unsigned int total_len, int err); struct sk_buff *tipc_port_get_ports(void); void tipc_port_recv_proto_msg(struct sk_buff *buf); void tipc_port_recv_mcast(struct sk_buff *buf, struct port_list *dp); diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 29d94d53198..33883739664 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -535,6 +535,9 @@ static int send_msg(struct kiocb *iocb, struct socket *sock, if (unlikely((m->msg_namelen < sizeof(*dest)) || (dest->family != AF_TIPC))) return -EINVAL; + if ((total_len > TIPC_MAX_USER_MSG_SIZE) || + (m->msg_iovlen > (unsigned)INT_MAX)) + return -EMSGSIZE; if (iocb) lock_sock(sk); @@ -573,12 +576,14 @@ static int send_msg(struct kiocb *iocb, struct socket *sock, &dest->addr.name.name, dest->addr.name.domain, m->msg_iovlen, - m->msg_iov); + m->msg_iov, + total_len); } else if (dest->addrtype == TIPC_ADDR_ID) { res = tipc_send2port(tport->ref, &dest->addr.id, m->msg_iovlen, - m->msg_iov); + m->msg_iov, + total_len); } else if (dest->addrtype == TIPC_ADDR_MCAST) { if (needs_conn) { res = -EOPNOTSUPP; @@ -590,7 +595,8 @@ static int send_msg(struct kiocb *iocb, struct socket *sock, res = tipc_multicast(tport->ref, &dest->addr.nameseq, m->msg_iovlen, - m->msg_iov); + m->msg_iov, + total_len); } if (likely(res != -ELINKCONG)) { if (needs_conn && (res >= 0)) @@ -640,6 +646,10 @@ static int send_packet(struct kiocb *iocb, struct socket *sock, if (unlikely(dest)) return send_msg(iocb, sock, m, total_len); + if ((total_len > TIPC_MAX_USER_MSG_SIZE) || + (m->msg_iovlen > (unsigned)INT_MAX)) + return -EMSGSIZE; + if (iocb) lock_sock(sk); @@ -652,7 +662,8 @@ static int send_packet(struct kiocb *iocb, struct socket *sock, break; } - res = tipc_send(tport->ref, m->msg_iovlen, m->msg_iov); + res = tipc_send(tport->ref, m->msg_iovlen, m->msg_iov, + total_len); if (likely(res != -ELINKCONG)) break; if (m->msg_flags & MSG_DONTWAIT) { @@ -723,6 +734,12 @@ static int send_stream(struct kiocb *iocb, struct socket *sock, goto exit; } + if ((total_len > (unsigned)INT_MAX) || + (m->msg_iovlen > (unsigned)INT_MAX)) { + res = -EMSGSIZE; + goto exit; + } + /* * Send each iovec entry using one or more messages * @@ -753,7 +770,7 @@ static int send_stream(struct kiocb *iocb, struct socket *sock, bytes_to_send = curr_left; my_iov.iov_base = curr_start; my_iov.iov_len = bytes_to_send; - res = send_packet(NULL, sock, &my_msg, 0); + res = send_packet(NULL, sock, &my_msg, bytes_to_send); if (res < 0) { if (bytes_sent) res = bytes_sent; diff --git a/net/tipc/subscr.c b/net/tipc/subscr.c index aae9eae1340..6cf72686348 100644 --- a/net/tipc/subscr.c +++ b/net/tipc/subscr.c @@ -109,7 +109,7 @@ static void subscr_send_event(struct subscription *sub, sub->evt.found_upper = htohl(found_upper, sub->swap); sub->evt.port.ref = htohl(port_ref, sub->swap); sub->evt.port.node = htohl(node, sub->swap); - tipc_send(sub->server_ref, 1, &msg_sect); + tipc_send(sub->server_ref, 1, &msg_sect, msg_sect.iov_len); } /** @@ -521,7 +521,7 @@ static void subscr_named_msg_event(void *usr_handle, /* Send an ACK- to complete connection handshaking */ - tipc_send(server_port_ref, 0, NULL); + tipc_send(server_port_ref, 0, NULL, 0); /* Handle optional subscription request */ diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 3a43a830476..0722a25a3a3 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -524,6 +524,8 @@ static int unix_dgram_connect(struct socket *, struct sockaddr *, int, int); static int unix_seqpacket_sendmsg(struct kiocb *, struct socket *, struct msghdr *, size_t); +static int unix_seqpacket_recvmsg(struct kiocb *, struct socket *, + struct msghdr *, size_t, int); static const struct proto_ops unix_stream_ops = { .family = PF_UNIX, @@ -583,7 +585,7 @@ static const struct proto_ops unix_seqpacket_ops = { .setsockopt = sock_no_setsockopt, .getsockopt = sock_no_getsockopt, .sendmsg = unix_seqpacket_sendmsg, - .recvmsg = unix_dgram_recvmsg, + .recvmsg = unix_seqpacket_recvmsg, .mmap = sock_no_mmap, .sendpage = sock_no_sendpage, }; @@ -1699,6 +1701,18 @@ static int unix_seqpacket_sendmsg(struct kiocb *kiocb, struct socket *sock, return unix_dgram_sendmsg(kiocb, sock, msg, len); } +static int unix_seqpacket_recvmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t size, + int flags) +{ + struct sock *sk = sock->sk; + + if (sk->sk_state != TCP_ESTABLISHED) + return -ENOTCONN; + + return unix_dgram_recvmsg(iocb, sock, msg, size, flags); +} + static void unix_copy_addr(struct msghdr *msg, struct sock *sk) { struct unix_sock *u = unix_sk(sk); @@ -2240,7 +2254,7 @@ static int unix_seq_show(struct seq_file *seq, void *v) struct unix_sock *u = unix_sk(s); unix_state_lock(s); - seq_printf(seq, "%p: %08X %08X %08X %04X %02X %5lu", + seq_printf(seq, "%pK: %08X %08X %08X %04X %02X %5lu", s, atomic_read(&s->sk_refcnt), 0, diff --git a/net/wireless/core.c b/net/wireless/core.c index fe01de29bfe..c22ef3492ee 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -46,6 +46,11 @@ static struct dentry *ieee80211_debugfs_dir; /* for the cleanup, scan and event works */ struct workqueue_struct *cfg80211_wq; +static bool cfg80211_disable_40mhz_24ghz; +module_param(cfg80211_disable_40mhz_24ghz, bool, 0644); +MODULE_PARM_DESC(cfg80211_disable_40mhz_24ghz, + "Disable 40MHz support in the 2.4GHz band"); + /* requires cfg80211_mutex to be held! */ struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx) { @@ -365,7 +370,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) spin_lock_init(&rdev->bss_lock); INIT_LIST_HEAD(&rdev->bss_list); INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done); - + INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results); #ifdef CONFIG_CFG80211_WEXT rdev->wiphy.wext = &cfg80211_wext_handler; #endif @@ -411,6 +416,67 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) } EXPORT_SYMBOL(wiphy_new); +static int wiphy_verify_combinations(struct wiphy *wiphy) +{ + const struct ieee80211_iface_combination *c; + int i, j; + + /* If we have combinations enforce them */ + if (wiphy->n_iface_combinations) + wiphy->flags |= WIPHY_FLAG_ENFORCE_COMBINATIONS; + + for (i = 0; i < wiphy->n_iface_combinations; i++) { + u32 cnt = 0; + u16 all_iftypes = 0; + + c = &wiphy->iface_combinations[i]; + + /* Combinations with just one interface aren't real */ + if (WARN_ON(c->max_interfaces < 2)) + return -EINVAL; + + /* Need at least one channel */ + if (WARN_ON(!c->num_different_channels)) + return -EINVAL; + + if (WARN_ON(!c->n_limits)) + return -EINVAL; + + for (j = 0; j < c->n_limits; j++) { + u16 types = c->limits[j].types; + + /* + * interface types shouldn't overlap, this is + * used in cfg80211_can_change_interface() + */ + if (WARN_ON(types & all_iftypes)) + return -EINVAL; + all_iftypes |= types; + + if (WARN_ON(!c->limits[j].max)) + return -EINVAL; + + /* Shouldn't list software iftypes in combinations! */ + if (WARN_ON(wiphy->software_iftypes & types)) + return -EINVAL; + + cnt += c->limits[j].max; + /* + * Don't advertise an unsupported type + * in a combination. + */ + if (WARN_ON((wiphy->interface_modes & types) != types)) + return -EINVAL; + } + + /* You can't even choose that many! */ + if (WARN_ON(cnt < c->max_interfaces)) + return -EINVAL; + } + + return 0; +} + int wiphy_register(struct wiphy *wiphy) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); @@ -439,6 +505,10 @@ int wiphy_register(struct wiphy *wiphy) if (WARN_ON(ifmodes != wiphy->interface_modes)) wiphy->interface_modes = ifmodes; + res = wiphy_verify_combinations(wiphy); + if (res) + return res; + /* sanity check supported bands/channels */ for (band = 0; band < IEEE80211_NUM_BANDS; band++) { sband = wiphy->bands[band]; @@ -451,6 +521,18 @@ int wiphy_register(struct wiphy *wiphy) return -EINVAL; /* + * Since cfg80211_disable_40mhz_24ghz is global, we can + * modify the sband's ht data even if the driver uses a + * global structure for that. + */ + if (cfg80211_disable_40mhz_24ghz && + band == IEEE80211_BAND_2GHZ && + sband->ht_cap.ht_supported) { + sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40; + } + + /* * Since we use a u32 for rate bitmaps in * ieee80211_get_response_rate, we cannot * have more than 32 legacy rates. @@ -476,6 +558,13 @@ int wiphy_register(struct wiphy *wiphy) return -EINVAL; } + if (rdev->wiphy.wowlan.n_patterns) { + if (WARN_ON(!rdev->wiphy.wowlan.pattern_min_len || + rdev->wiphy.wowlan.pattern_min_len > + rdev->wiphy.wowlan.pattern_max_len)) + return -EINVAL; + } + /* check and set up bitrates */ ieee80211_set_bitrate_flags(wiphy); @@ -614,6 +703,7 @@ void cfg80211_dev_free(struct cfg80211_registered_device *rdev) mutex_destroy(&rdev->devlist_mtx); list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list) cfg80211_put_bss(&scan->pub); + cfg80211_rdev_free_wowlan(rdev); kfree(rdev); } @@ -647,6 +737,11 @@ static void wdev_cleanup_work(struct work_struct *work) ___cfg80211_scan_done(rdev, true); } + if (WARN_ON(rdev->sched_scan_req && + rdev->sched_scan_req->dev == wdev->netdev)) { + __cfg80211_stop_sched_scan(rdev, false); + } + cfg80211_unlock_rdev(rdev); mutex_lock(&rdev->devlist_mtx); @@ -668,6 +763,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, struct net_device *dev = ndev; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev; + int ret; if (!wdev) return NOTIFY_DONE; @@ -734,6 +830,10 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_STATION: + cfg80211_lock_rdev(rdev); + __cfg80211_stop_sched_scan(rdev, false); + cfg80211_unlock_rdev(rdev); + wdev_lock(wdev); #ifdef CONFIG_CFG80211_WEXT kfree(wdev->wext.ie); @@ -752,6 +852,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, default: break; } + wdev->beacon_interval = 0; break; case NETDEV_DOWN: dev_hold(dev); @@ -858,6 +959,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, return notifier_from_errno(-EOPNOTSUPP); if (rfkill_blocked(rdev->rfkill)) return notifier_from_errno(-ERFKILL); + ret = cfg80211_can_add_interface(rdev, wdev->iftype); + if (ret) + return notifier_from_errno(ret); break; } diff --git a/net/wireless/core.h b/net/wireless/core.h index 26a0a084e16..3dce1f167eb 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -60,8 +60,10 @@ struct cfg80211_registered_device { struct rb_root bss_tree; u32 bss_generation; struct cfg80211_scan_request *scan_req; /* protected by RTNL */ + struct cfg80211_sched_scan_request *sched_scan_req; unsigned long suspend_at; struct work_struct scan_done_wk; + struct work_struct sched_scan_results_wk; #ifdef CONFIG_NL80211_TESTMODE struct genl_info *testmode_info; @@ -70,6 +72,8 @@ struct cfg80211_registered_device { struct work_struct conn_work; struct work_struct event_work; + struct cfg80211_wowlan *wowlan; + /* must be last because of the way we do wiphy_priv(), * and it should at least be aligned to NETDEV_ALIGN */ struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN))); @@ -89,6 +93,18 @@ bool wiphy_idx_valid(int wiphy_idx) return wiphy_idx >= 0; } +static inline void +cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev) +{ + int i; + + if (!rdev->wowlan) + return; + for (i = 0; i < rdev->wowlan->n_patterns; i++) + kfree(rdev->wowlan->patterns[i].mask); + kfree(rdev->wowlan->patterns); + kfree(rdev->wowlan); +} extern struct workqueue_struct *cfg80211_wq; extern struct mutex cfg80211_mutex; @@ -229,6 +245,7 @@ struct cfg80211_event { u16 status; } cr; struct { + struct ieee80211_channel *channel; u8 bssid[ETH_ALEN]; const u8 *req_ie; const u8 *resp_ie; @@ -376,7 +393,9 @@ int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, int cfg80211_disconnect(struct cfg80211_registered_device *rdev, struct net_device *dev, u16 reason, bool wextev); -void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, +void __cfg80211_roamed(struct wireless_dev *wdev, + struct ieee80211_channel *channel, + const u8 *bssid, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len); int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, @@ -397,12 +416,26 @@ void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len); void cfg80211_sme_disassoc(struct net_device *dev, int idx); void __cfg80211_scan_done(struct work_struct *wk); void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak); +void __cfg80211_sched_scan_results(struct work_struct *wk); +int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, + bool driver_initiated); void cfg80211_upload_connect_keys(struct wireless_dev *wdev); int cfg80211_change_iface(struct cfg80211_registered_device *rdev, struct net_device *dev, enum nl80211_iftype ntype, u32 *flags, struct vif_params *params); void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev); +int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + enum nl80211_iftype iftype); + +static inline int +cfg80211_can_add_interface(struct cfg80211_registered_device *rdev, + enum nl80211_iftype iftype) +{ + return cfg80211_can_change_interface(rdev, NULL, iftype); +} + struct ieee80211_channel * rdev_freq_to_chan(struct cfg80211_registered_device *rdev, int freq, enum nl80211_channel_type channel_type); @@ -412,6 +445,9 @@ int cfg80211_set_freq(struct cfg80211_registered_device *rdev, u16 cfg80211_calculate_bitrate(struct rate_info *rate); +int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, + u32 beacon_int); + #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS #define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond) #else diff --git a/net/wireless/lib80211_crypt_wep.c b/net/wireless/lib80211_crypt_wep.c index e2e88878ba3..2f265e033ae 100644 --- a/net/wireless/lib80211_crypt_wep.c +++ b/net/wireless/lib80211_crypt_wep.c @@ -96,13 +96,12 @@ static int lib80211_wep_build_iv(struct sk_buff *skb, int hdr_len, u8 *key, int keylen, void *priv) { struct lib80211_wep_data *wep = priv; - u32 klen, len; + u32 klen; u8 *pos; if (skb_headroom(skb) < 4 || skb->len < hdr_len) return -1; - len = skb->len - hdr_len; pos = skb_push(skb, 4); memmove(pos, pos + 4, hdr_len); pos += hdr_len; diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 73e39c171ff..5c116083eec 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -1,5 +1,6 @@ #include <linux/ieee80211.h> #include <net/cfg80211.h> +#include "nl80211.h" #include "core.h" /* Default values, timeouts in ms */ @@ -53,8 +54,9 @@ const struct mesh_config default_mesh_config = { const struct mesh_setup default_mesh_setup = { .path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP, .path_metric = IEEE80211_PATH_METRIC_AIRTIME, - .vendor_ie = NULL, - .vendor_ie_len = 0, + .ie = NULL, + .ie_len = 0, + .is_secure = false, }; int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, @@ -72,6 +74,10 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) return -EOPNOTSUPP; + if (!(rdev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) && + setup->is_secure) + return -EOPNOTSUPP; + if (wdev->mesh_id_len) return -EALREADY; @@ -105,6 +111,19 @@ int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, return err; } +void cfg80211_notify_new_peer_candidate(struct net_device *dev, + const u8 *macaddr, const u8* ie, u8 ie_len, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_MESH_POINT)) + return; + + nl80211_send_new_peer_candidate(wiphy_to_dev(wdev->wiphy), dev, + macaddr, ie, ie_len, gfp); +} +EXPORT_SYMBOL(cfg80211_notify_new_peer_candidate); + static int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, struct net_device *dev) { diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 16881fea4ce..493b939970c 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -963,6 +963,16 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, if (memcmp(mgmt->bssid, dev->dev_addr, ETH_ALEN)) err = -EINVAL; break; + case NL80211_IFTYPE_MESH_POINT: + if (memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN)) { + err = -EINVAL; + break; + } + /* + * check for mesh DA must be done by driver as + * cfg80211 doesn't track the stations + */ + break; default: err = -EOPNOTSUPP; break; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 297d7ce4117..70cbc8ca371 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -124,6 +124,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_BSS_HT_OPMODE] = { .type = NLA_U16 }, [NL80211_ATTR_MESH_CONFIG] = { .type = NLA_NESTED }, + [NL80211_ATTR_SUPPORT_MESH_AUTH] = { .type = NLA_FLAG }, [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY, .len = NL80211_HT_CAPABILITY_LEN }, @@ -172,6 +173,9 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 }, [NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG }, [NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED }, + [NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED }, + [NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 }, + [NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 }, }; /* policy for the key attributes */ @@ -193,6 +197,15 @@ nl80211_key_default_policy[NUM_NL80211_KEY_DEFAULT_TYPES] = { [NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG }, }; +/* policy for WoWLAN attributes */ +static const struct nla_policy +nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = { + [NL80211_WOWLAN_TRIG_ANY] = { .type = NLA_FLAG }, + [NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG }, + [NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG }, + [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED }, +}; + /* ifidx get helper */ static int nl80211_get_ifidx(struct netlink_callback *cb) { @@ -533,6 +546,7 @@ static int nl80211_key_allowed(struct wireless_dev *wdev) case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_MESH_POINT: break; case NL80211_IFTYPE_ADHOC: if (!wdev->current_bss) @@ -550,6 +564,88 @@ static int nl80211_key_allowed(struct wireless_dev *wdev) return 0; } +static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes) +{ + struct nlattr *nl_modes = nla_nest_start(msg, attr); + int i; + + if (!nl_modes) + goto nla_put_failure; + + i = 0; + while (ifmodes) { + if (ifmodes & 1) + NLA_PUT_FLAG(msg, i); + ifmodes >>= 1; + i++; + } + + nla_nest_end(msg, nl_modes); + return 0; + +nla_put_failure: + return -ENOBUFS; +} + +static int nl80211_put_iface_combinations(struct wiphy *wiphy, + struct sk_buff *msg) +{ + struct nlattr *nl_combis; + int i, j; + + nl_combis = nla_nest_start(msg, + NL80211_ATTR_INTERFACE_COMBINATIONS); + if (!nl_combis) + goto nla_put_failure; + + for (i = 0; i < wiphy->n_iface_combinations; i++) { + const struct ieee80211_iface_combination *c; + struct nlattr *nl_combi, *nl_limits; + + c = &wiphy->iface_combinations[i]; + + nl_combi = nla_nest_start(msg, i + 1); + if (!nl_combi) + goto nla_put_failure; + + nl_limits = nla_nest_start(msg, NL80211_IFACE_COMB_LIMITS); + if (!nl_limits) + goto nla_put_failure; + + for (j = 0; j < c->n_limits; j++) { + struct nlattr *nl_limit; + + nl_limit = nla_nest_start(msg, j + 1); + if (!nl_limit) + goto nla_put_failure; + NLA_PUT_U32(msg, NL80211_IFACE_LIMIT_MAX, + c->limits[j].max); + if (nl80211_put_iftypes(msg, NL80211_IFACE_LIMIT_TYPES, + c->limits[j].types)) + goto nla_put_failure; + nla_nest_end(msg, nl_limit); + } + + nla_nest_end(msg, nl_limits); + + if (c->beacon_int_infra_match) + NLA_PUT_FLAG(msg, + NL80211_IFACE_COMB_STA_AP_BI_MATCH); + NLA_PUT_U32(msg, NL80211_IFACE_COMB_NUM_CHANNELS, + c->num_different_channels); + NLA_PUT_U32(msg, NL80211_IFACE_COMB_MAXNUM, + c->max_interfaces); + + nla_nest_end(msg, nl_combi); + } + + nla_nest_end(msg, nl_combis); + + return 0; +nla_put_failure: + return -ENOBUFS; +} + static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, struct cfg80211_registered_device *dev) { @@ -557,13 +653,11 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, struct nlattr *nl_bands, *nl_band; struct nlattr *nl_freqs, *nl_freq; struct nlattr *nl_rates, *nl_rate; - struct nlattr *nl_modes; struct nlattr *nl_cmds; enum ieee80211_band band; struct ieee80211_channel *chan; struct ieee80211_rate *rate; int i; - u16 ifmodes = dev->wiphy.interface_modes; const struct ieee80211_txrx_stypes *mgmt_stypes = dev->wiphy.mgmt_stypes; @@ -594,6 +688,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, if (dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_IBSS_RSN); + if (dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) + NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_MESH_AUTH); NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES, sizeof(u32) * dev->wiphy.n_cipher_suites, @@ -621,20 +717,10 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, } } - nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES); - if (!nl_modes) + if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES, + dev->wiphy.interface_modes)) goto nla_put_failure; - i = 0; - while (ifmodes) { - if (ifmodes & 1) - NLA_PUT_FLAG(msg, i); - ifmodes >>= 1; - i++; - } - - nla_nest_end(msg, nl_modes); - nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS); if (!nl_bands) goto nla_put_failure; @@ -746,6 +832,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, } CMD(set_channel, SET_CHANNEL); CMD(set_wds_peer, SET_WDS_PEER); + if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) + CMD(sched_scan_start, START_SCHED_SCAN); #undef CMD @@ -818,6 +906,42 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, nla_nest_end(msg, nl_ifs); } + if (dev->wiphy.wowlan.flags || dev->wiphy.wowlan.n_patterns) { + struct nlattr *nl_wowlan; + + nl_wowlan = nla_nest_start(msg, + NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED); + if (!nl_wowlan) + goto nla_put_failure; + + if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY); + if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT); + if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT); + if (dev->wiphy.wowlan.n_patterns) { + struct nl80211_wowlan_pattern_support pat = { + .max_patterns = dev->wiphy.wowlan.n_patterns, + .min_pattern_len = + dev->wiphy.wowlan.pattern_min_len, + .max_pattern_len = + dev->wiphy.wowlan.pattern_max_len, + }; + NLA_PUT(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN, + sizeof(pat), &pat); + } + + nla_nest_end(msg, nl_wowlan); + } + + if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES, + dev->wiphy.software_iftypes)) + goto nla_put_failure; + + if (nl80211_put_iface_combinations(&dev->wiphy, msg)) + goto nla_put_failure; + return genlmsg_end(msg, hdr); nla_put_failure: @@ -1679,14 +1803,6 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) if (err) goto out; - if (!(rdev->wiphy.flags & - WIPHY_FLAG_SUPPORTS_SEPARATE_DEFAULT_KEYS)) { - if (!key.def_uni || !key.def_multi) { - err = -EOPNOTSUPP; - goto out; - } - } - err = rdev->ops->set_default_key(&rdev->wiphy, dev, key.idx, key.def_uni, key.def_multi); @@ -1837,8 +1953,9 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) struct beacon_parameters *info); struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; struct beacon_parameters params; - int haveinfo = 0; + int haveinfo = 0, err; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL])) return -EINVAL; @@ -1847,6 +1964,8 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) return -EOPNOTSUPP; + memset(¶ms, 0, sizeof(params)); + switch (info->genlhdr->cmd) { case NL80211_CMD_NEW_BEACON: /* these are required for NEW_BEACON */ @@ -1855,6 +1974,15 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) !info->attrs[NL80211_ATTR_BEACON_HEAD]) return -EINVAL; + params.interval = + nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); + params.dtim_period = + nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]); + + err = cfg80211_validate_beacon_int(rdev, params.interval); + if (err) + return err; + call = rdev->ops->add_beacon; break; case NL80211_CMD_SET_BEACON: @@ -1868,20 +1996,6 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) if (!call) return -EOPNOTSUPP; - memset(¶ms, 0, sizeof(params)); - - if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) { - params.interval = - nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); - haveinfo = 1; - } - - if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) { - params.dtim_period = - nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]); - haveinfo = 1; - } - if (info->attrs[NL80211_ATTR_BEACON_HEAD]) { params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]); params.head_len = @@ -1899,13 +2013,18 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) if (!haveinfo) return -EINVAL; - return call(&rdev->wiphy, dev, ¶ms); + err = call(&rdev->wiphy, dev, ¶ms); + if (!err && params.interval) + wdev->beacon_interval = params.interval; + return err; } static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; if (!rdev->ops->del_beacon) return -EOPNOTSUPP; @@ -1914,7 +2033,10 @@ static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info) dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) return -EOPNOTSUPP; - return rdev->ops->del_beacon(&rdev->wiphy, dev); + err = rdev->ops->del_beacon(&rdev->wiphy, dev); + if (!err) + wdev->beacon_interval = 0; + return err; } static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = { @@ -1922,6 +2044,7 @@ static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = { [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG }, [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG }, [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG }, + [NL80211_STA_FLAG_AUTHENTICATED] = { .type = NLA_FLAG }, }; static int parse_station_flags(struct genl_info *info, @@ -2016,6 +2139,9 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO); if (!sinfoattr) goto nla_put_failure; + if (sinfo->filled & STATION_INFO_CONNECTED_TIME) + NLA_PUT_U32(msg, NL80211_STA_INFO_CONNECTED_TIME, + sinfo->connected_time); if (sinfo->filled & STATION_INFO_INACTIVE_TIME) NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME, sinfo->inactive_time); @@ -2209,6 +2335,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) memset(¶ms, 0, sizeof(params)); params.listen_interval = -1; + params.plink_state = -1; if (info->attrs[NL80211_ATTR_STA_AID]) return -EINVAL; @@ -2240,6 +2367,10 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) params.plink_action = nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); + if (info->attrs[NL80211_ATTR_STA_PLINK_STATE]) + params.plink_state = + nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]); + err = get_vlan(info, rdev, ¶ms.vlan); if (err) goto out; @@ -2279,9 +2410,10 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) err = -EINVAL; if (params.listen_interval >= 0) err = -EINVAL; - if (params.supported_rates) - err = -EINVAL; - if (params.sta_flags_mask) + if (params.sta_flags_mask & + ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) | + BIT(NL80211_STA_FLAG_MFP) | + BIT(NL80211_STA_FLAG_AUTHORIZED))) err = -EINVAL; break; default: @@ -2343,11 +2475,16 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) params.ht_capa = nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); + if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) + params.plink_action = + nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); + if (parse_station_flags(info, ¶ms)) return -EINVAL; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) return -EINVAL; @@ -2823,8 +2960,10 @@ static const struct nla_policy nl80211_mesh_setup_params_policy[NL80211_MESH_SETUP_ATTR_MAX+1] = { [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 }, [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 }, - [NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE] = { .type = NLA_BINARY, + [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG }, + [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY, .len = IEEE80211_MAX_DATA_LEN }, + [NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG }, }; static int nl80211_parse_mesh_config(struct genl_info *info, @@ -2925,14 +3064,17 @@ static int nl80211_parse_mesh_setup(struct genl_info *info, IEEE80211_PATH_METRIC_VENDOR : IEEE80211_PATH_METRIC_AIRTIME; - if (tb[NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE]) { + + if (tb[NL80211_MESH_SETUP_IE]) { struct nlattr *ieattr = - tb[NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE]; + tb[NL80211_MESH_SETUP_IE]; if (!is_valid_ie_attr(ieattr)) return -EINVAL; - setup->vendor_ie = nla_data(ieattr); - setup->vendor_ie_len = nla_len(ieattr); + setup->ie = nla_data(ieattr); + setup->ie_len = nla_len(ieattr); } + setup->is_authenticated = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]); + setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AMPE]); return 0; } @@ -3152,8 +3294,6 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct cfg80211_scan_request *request; - struct cfg80211_ssid *ssid; - struct ieee80211_channel *channel; struct nlattr *attr; struct wiphy *wiphy; int err, tmp, n_ssids = 0, n_channels, i; @@ -3200,8 +3340,8 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) return -EINVAL; request = kzalloc(sizeof(*request) - + sizeof(*ssid) * n_ssids - + sizeof(channel) * n_channels + + sizeof(*request->ssids) * n_ssids + + sizeof(*request->channels) * n_channels + ie_len, GFP_KERNEL); if (!request) return -ENOMEM; @@ -3266,12 +3406,12 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) i = 0; if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) { nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) { + request->ssids[i].ssid_len = nla_len(attr); if (request->ssids[i].ssid_len > IEEE80211_MAX_SSID_LEN) { err = -EINVAL; goto out_free; } memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr)); - request->ssids[i].ssid_len = nla_len(attr); i++; } } @@ -3301,6 +3441,186 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) return err; } +static int nl80211_start_sched_scan(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_sched_scan_request *request; + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct nlattr *attr; + struct wiphy *wiphy; + int err, tmp, n_ssids = 0, n_channels, i; + u32 interval; + enum ieee80211_band band; + size_t ie_len; + + if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || + !rdev->ops->sched_scan_start) + return -EOPNOTSUPP; + + if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) + return -EINVAL; + + if (rdev->sched_scan_req) + return -EINPROGRESS; + + if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]) + return -EINVAL; + + interval = nla_get_u32(info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]); + if (interval == 0) + return -EINVAL; + + wiphy = &rdev->wiphy; + + if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { + n_channels = validate_scan_freqs( + info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]); + if (!n_channels) + return -EINVAL; + } else { + n_channels = 0; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) + if (wiphy->bands[band]) + n_channels += wiphy->bands[band]->n_channels; + } + + if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) + nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], + tmp) + n_ssids++; + + if (n_ssids > wiphy->max_scan_ssids) + return -EINVAL; + + if (info->attrs[NL80211_ATTR_IE]) + ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + else + ie_len = 0; + + if (ie_len > wiphy->max_scan_ie_len) + return -EINVAL; + + request = kzalloc(sizeof(*request) + + sizeof(*request->ssids) * n_ssids + + sizeof(*request->channels) * n_channels + + ie_len, GFP_KERNEL); + if (!request) + return -ENOMEM; + + if (n_ssids) + request->ssids = (void *)&request->channels[n_channels]; + request->n_ssids = n_ssids; + if (ie_len) { + if (request->ssids) + request->ie = (void *)(request->ssids + n_ssids); + else + request->ie = (void *)(request->channels + n_channels); + } + + i = 0; + if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { + /* user specified, bail out if channel not found */ + nla_for_each_nested(attr, + info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], + tmp) { + struct ieee80211_channel *chan; + + chan = ieee80211_get_channel(wiphy, nla_get_u32(attr)); + + if (!chan) { + err = -EINVAL; + goto out_free; + } + + /* ignore disabled channels */ + if (chan->flags & IEEE80211_CHAN_DISABLED) + continue; + + request->channels[i] = chan; + i++; + } + } else { + /* all channels */ + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + int j; + if (!wiphy->bands[band]) + continue; + for (j = 0; j < wiphy->bands[band]->n_channels; j++) { + struct ieee80211_channel *chan; + + chan = &wiphy->bands[band]->channels[j]; + + if (chan->flags & IEEE80211_CHAN_DISABLED) + continue; + + request->channels[i] = chan; + i++; + } + } + } + + if (!i) { + err = -EINVAL; + goto out_free; + } + + request->n_channels = i; + + i = 0; + if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) { + nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], + tmp) { + request->ssids[i].ssid_len = nla_len(attr); + if (request->ssids[i].ssid_len > + IEEE80211_MAX_SSID_LEN) { + err = -EINVAL; + goto out_free; + } + memcpy(request->ssids[i].ssid, nla_data(attr), + nla_len(attr)); + i++; + } + } + + if (info->attrs[NL80211_ATTR_IE]) { + request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + memcpy((void *)request->ie, + nla_data(info->attrs[NL80211_ATTR_IE]), + request->ie_len); + } + + request->dev = dev; + request->wiphy = &rdev->wiphy; + request->interval = interval; + + err = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request); + if (!err) { + rdev->sched_scan_req = request; + nl80211_send_sched_scan(rdev, dev, + NL80211_CMD_START_SCHED_SCAN); + goto out; + } + +out_free: + kfree(request); +out: + return err; +} + +static int nl80211_stop_sched_scan(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + + if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || + !rdev->ops->sched_scan_stop) + return -EOPNOTSUPP; + + return __cfg80211_stop_sched_scan(rdev, false); +} + static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, @@ -3434,10 +3754,6 @@ static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq, void *hdr; struct nlattr *infoattr; - /* Survey without a channel doesn't make sense */ - if (!survey->channel) - return -EINVAL; - hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_SURVEY_RESULTS); if (!hdr) @@ -3500,6 +3816,8 @@ static int nl80211_dump_survey(struct sk_buff *skb, } while (1) { + struct ieee80211_channel *chan; + res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx, &survey); if (res == -ENOENT) @@ -3507,6 +3825,19 @@ static int nl80211_dump_survey(struct sk_buff *skb, if (res) goto out_err; + /* Survey without a channel doesn't make sense */ + if (!survey.channel) { + res = -EINVAL; + goto out; + } + + chan = ieee80211_get_channel(&dev->wiphy, + survey.channel->center_freq); + if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) { + survey_idx++; + continue; + } + if (nl80211_send_survey(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, NLM_F_MULTI, @@ -4041,6 +4372,93 @@ static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info) return err; } +static int nl80211_testmode_dump(struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct cfg80211_registered_device *dev; + int err; + long phy_idx; + void *data = NULL; + int data_len = 0; + + if (cb->args[0]) { + /* + * 0 is a valid index, but not valid for args[0], + * so we need to offset by 1. + */ + phy_idx = cb->args[0] - 1; + } else { + err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, + nl80211_fam.attrbuf, nl80211_fam.maxattr, + nl80211_policy); + if (err) + return err; + if (!nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]) + return -EINVAL; + phy_idx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]); + if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA]) + cb->args[1] = + (long)nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA]; + } + + if (cb->args[1]) { + data = nla_data((void *)cb->args[1]); + data_len = nla_len((void *)cb->args[1]); + } + + mutex_lock(&cfg80211_mutex); + dev = cfg80211_rdev_by_wiphy_idx(phy_idx); + if (!dev) { + mutex_unlock(&cfg80211_mutex); + return -ENOENT; + } + cfg80211_lock_rdev(dev); + mutex_unlock(&cfg80211_mutex); + + if (!dev->ops->testmode_dump) { + err = -EOPNOTSUPP; + goto out_err; + } + + while (1) { + void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + NL80211_CMD_TESTMODE); + struct nlattr *tmdata; + + if (nla_put_u32(skb, NL80211_ATTR_WIPHY, dev->wiphy_idx) < 0) { + genlmsg_cancel(skb, hdr); + break; + } + + tmdata = nla_nest_start(skb, NL80211_ATTR_TESTDATA); + if (!tmdata) { + genlmsg_cancel(skb, hdr); + break; + } + err = dev->ops->testmode_dump(&dev->wiphy, skb, cb, + data, data_len); + nla_nest_end(skb, tmdata); + + if (err == -ENOBUFS || err == -ENOENT) { + genlmsg_cancel(skb, hdr); + break; + } else if (err) { + genlmsg_cancel(skb, hdr); + goto out_err; + } + + genlmsg_end(skb, hdr); + } + + err = skb->len; + /* see above */ + cb->args[0] = phy_idx + 1; + out_err: + cfg80211_unlock_rdev(dev); + return err; +} + static struct sk_buff * __cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev, int approxlen, u32 pid, u32 seq, gfp_t gfp) @@ -4799,6 +5217,194 @@ static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info) return cfg80211_leave_mesh(rdev, dev); } +static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct sk_buff *msg; + void *hdr; + + if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns) + return -EOPNOTSUPP; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, + NL80211_CMD_GET_WOWLAN); + if (!hdr) + goto nla_put_failure; + + if (rdev->wowlan) { + struct nlattr *nl_wowlan; + + nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS); + if (!nl_wowlan) + goto nla_put_failure; + + if (rdev->wowlan->any) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY); + if (rdev->wowlan->disconnect) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT); + if (rdev->wowlan->magic_pkt) + NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT); + if (rdev->wowlan->n_patterns) { + struct nlattr *nl_pats, *nl_pat; + int i, pat_len; + + nl_pats = nla_nest_start(msg, + NL80211_WOWLAN_TRIG_PKT_PATTERN); + if (!nl_pats) + goto nla_put_failure; + + for (i = 0; i < rdev->wowlan->n_patterns; i++) { + nl_pat = nla_nest_start(msg, i + 1); + if (!nl_pat) + goto nla_put_failure; + pat_len = rdev->wowlan->patterns[i].pattern_len; + NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_MASK, + DIV_ROUND_UP(pat_len, 8), + rdev->wowlan->patterns[i].mask); + NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_PATTERN, + pat_len, + rdev->wowlan->patterns[i].pattern); + nla_nest_end(msg, nl_pat); + } + nla_nest_end(msg, nl_pats); + } + + nla_nest_end(msg, nl_wowlan); + } + + genlmsg_end(msg, hdr); + return genlmsg_reply(msg, info); + +nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + +static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG]; + struct cfg80211_wowlan no_triggers = {}; + struct cfg80211_wowlan new_triggers = {}; + struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan; + int err, i; + + if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns) + return -EOPNOTSUPP; + + if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) + goto no_triggers; + + err = nla_parse(tb, MAX_NL80211_WOWLAN_TRIG, + nla_data(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]), + nla_len(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]), + nl80211_wowlan_policy); + if (err) + return err; + + if (tb[NL80211_WOWLAN_TRIG_ANY]) { + if (!(wowlan->flags & WIPHY_WOWLAN_ANY)) + return -EINVAL; + new_triggers.any = true; + } + + if (tb[NL80211_WOWLAN_TRIG_DISCONNECT]) { + if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT)) + return -EINVAL; + new_triggers.disconnect = true; + } + + if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) { + if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT)) + return -EINVAL; + new_triggers.magic_pkt = true; + } + + if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) { + struct nlattr *pat; + int n_patterns = 0; + int rem, pat_len, mask_len; + struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT]; + + nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], + rem) + n_patterns++; + if (n_patterns > wowlan->n_patterns) + return -EINVAL; + + new_triggers.patterns = kcalloc(n_patterns, + sizeof(new_triggers.patterns[0]), + GFP_KERNEL); + if (!new_triggers.patterns) + return -ENOMEM; + + new_triggers.n_patterns = n_patterns; + i = 0; + + nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], + rem) { + nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT, + nla_data(pat), nla_len(pat), NULL); + err = -EINVAL; + if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] || + !pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]) + goto error; + pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]); + mask_len = DIV_ROUND_UP(pat_len, 8); + if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) != + mask_len) + goto error; + if (pat_len > wowlan->pattern_max_len || + pat_len < wowlan->pattern_min_len) + goto error; + + new_triggers.patterns[i].mask = + kmalloc(mask_len + pat_len, GFP_KERNEL); + if (!new_triggers.patterns[i].mask) { + err = -ENOMEM; + goto error; + } + new_triggers.patterns[i].pattern = + new_triggers.patterns[i].mask + mask_len; + memcpy(new_triggers.patterns[i].mask, + nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]), + mask_len); + new_triggers.patterns[i].pattern_len = pat_len; + memcpy(new_triggers.patterns[i].pattern, + nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]), + pat_len); + i++; + } + } + + if (memcmp(&new_triggers, &no_triggers, sizeof(new_triggers))) { + struct cfg80211_wowlan *ntrig; + ntrig = kmemdup(&new_triggers, sizeof(new_triggers), + GFP_KERNEL); + if (!ntrig) { + err = -ENOMEM; + goto error; + } + cfg80211_rdev_free_wowlan(rdev); + rdev->wowlan = ntrig; + } else { + no_triggers: + cfg80211_rdev_free_wowlan(rdev); + rdev->wowlan = NULL; + } + + return 0; + error: + for (i = 0; i < new_triggers.n_patterns; i++) + kfree(new_triggers.patterns[i].mask); + kfree(new_triggers.patterns); + return err; +} + #define NL80211_FLAG_NEED_WIPHY 0x01 #define NL80211_FLAG_NEED_NETDEV 0x02 #define NL80211_FLAG_NEED_RTNL 0x04 @@ -5083,6 +5689,22 @@ static struct genl_ops nl80211_ops[] = { .dumpit = nl80211_dump_scan, }, { + .cmd = NL80211_CMD_START_SCHED_SCAN, + .doit = nl80211_start_sched_scan, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_STOP_SCHED_SCAN, + .doit = nl80211_stop_sched_scan, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { .cmd = NL80211_CMD_AUTHENTICATE, .doit = nl80211_authenticate, .policy = nl80211_policy, @@ -5134,6 +5756,7 @@ static struct genl_ops nl80211_ops[] = { { .cmd = NL80211_CMD_TESTMODE, .doit = nl80211_testmode_do, + .dumpit = nl80211_testmode_dump, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_WIPHY | @@ -5297,6 +5920,22 @@ static struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, + { + .cmd = NL80211_CMD_GET_WOWLAN, + .doit = nl80211_get_wowlan, + .policy = nl80211_policy, + /* can be retrieved by unprivileged users */ + .internal_flags = NL80211_FLAG_NEED_WIPHY | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_SET_WOWLAN, + .doit = nl80211_set_wowlan, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_WIPHY | + NL80211_FLAG_NEED_RTNL, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { @@ -5392,6 +6031,28 @@ static int nl80211_send_scan_msg(struct sk_buff *msg, return -EMSGSIZE; } +static int +nl80211_send_sched_scan_msg(struct sk_buff *msg, + struct cfg80211_registered_device *rdev, + struct net_device *netdev, + u32 pid, u32 seq, int flags, u32 cmd) +{ + void *hdr; + + hdr = nl80211hdr_put(msg, pid, seq, flags, cmd); + if (!hdr) + return -1; + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + + return genlmsg_end(msg, hdr); + + nla_put_failure: + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, struct net_device *netdev) { @@ -5449,6 +6110,43 @@ void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, nl80211_scan_mcgrp.id, GFP_KERNEL); } +void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev, + struct net_device *netdev) +{ + struct sk_buff *msg; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return; + + if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, + NL80211_CMD_SCHED_SCAN_RESULTS) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_scan_mcgrp.id, GFP_KERNEL); +} + +void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, + struct net_device *netdev, u32 cmd) +{ + struct sk_buff *msg; + + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) + return; + + if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_scan_mcgrp.id, GFP_KERNEL); +} + /* * This can happen on global regulatory changes or device specific settings * based on custom world regulatory domains. @@ -5804,6 +6502,44 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, nlmsg_free(msg); } +void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *macaddr, const u8* ie, u8 ie_len, + gfp_t gfp) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NEW_PEER_CANDIDATE); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, macaddr); + if (ie_len && ie) + NLA_PUT(msg, NL80211_ATTR_IE, ie_len , ie); + + if (genlmsg_end(msg, hdr) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *addr, enum nl80211_key_type key_type, int key_id, diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index dcac5cd6f01..2f1bfb87a65 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -12,6 +12,10 @@ void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, struct net_device *netdev); void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, struct net_device *netdev); +void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, + struct net_device *netdev, u32 cmd); +void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev, + struct net_device *netdev); void nl80211_send_reg_change_event(struct regulatory_request *request); void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, struct net_device *netdev, @@ -50,6 +54,10 @@ void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, struct net_device *netdev, u16 reason, const u8 *ie, size_t ie_len, bool from_ap); +void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *macaddr, const u8* ie, u8 ie_len, + gfp_t gfp); void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *addr, diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 58d69959ab2..1ad0f39fe09 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -672,11 +672,9 @@ static int freq_reg_info_regd(struct wiphy *wiphy, for (i = 0; i < regd->n_reg_rules; i++) { const struct ieee80211_reg_rule *rr; const struct ieee80211_freq_range *fr = NULL; - const struct ieee80211_power_rule *pr = NULL; rr = ®d->reg_rules[i]; fr = &rr->freq_range; - pr = &rr->power_rule; /* * We only need to know if one frequency rule was @@ -1455,7 +1453,8 @@ static void reg_process_hint(struct regulatory_request *reg_request) * We only time out user hints, given that they should be the only * source of bogus requests. */ - if (reg_request->initiator == NL80211_REGDOM_SET_BY_USER) + if (r != -EALREADY && + reg_request->initiator == NL80211_REGDOM_SET_BY_USER) schedule_delayed_work(®_timeout, msecs_to_jiffies(3142)); } diff --git a/net/wireless/scan.c b/net/wireless/scan.c index fbf6f33ae4d..7a6c67667d7 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -93,6 +93,69 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) } EXPORT_SYMBOL(cfg80211_scan_done); +void __cfg80211_sched_scan_results(struct work_struct *wk) +{ + struct cfg80211_registered_device *rdev; + + rdev = container_of(wk, struct cfg80211_registered_device, + sched_scan_results_wk); + + cfg80211_lock_rdev(rdev); + + /* we don't have sched_scan_req anymore if the scan is stopping */ + if (rdev->sched_scan_req) + nl80211_send_sched_scan_results(rdev, + rdev->sched_scan_req->dev); + + cfg80211_unlock_rdev(rdev); +} + +void cfg80211_sched_scan_results(struct wiphy *wiphy) +{ + /* ignore if we're not scanning */ + if (wiphy_to_dev(wiphy)->sched_scan_req) + queue_work(cfg80211_wq, + &wiphy_to_dev(wiphy)->sched_scan_results_wk); +} +EXPORT_SYMBOL(cfg80211_sched_scan_results); + +void cfg80211_sched_scan_stopped(struct wiphy *wiphy) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + cfg80211_lock_rdev(rdev); + __cfg80211_stop_sched_scan(rdev, true); + cfg80211_unlock_rdev(rdev); +} +EXPORT_SYMBOL(cfg80211_sched_scan_stopped); + +int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, + bool driver_initiated) +{ + int err; + struct net_device *dev; + + ASSERT_RDEV_LOCK(rdev); + + if (!rdev->sched_scan_req) + return 0; + + dev = rdev->sched_scan_req->dev; + + if (!driver_initiated) { + err = rdev->ops->sched_scan_stop(&rdev->wiphy, dev); + if (err) + return err; + } + + nl80211_send_sched_scan(rdev, dev, NL80211_CMD_SCHED_SCAN_STOPPED); + + kfree(rdev->sched_scan_req); + rdev->sched_scan_req = NULL; + + return err; +} + static void bss_release(struct kref *ref) { struct cfg80211_internal_bss *bss; @@ -204,13 +267,35 @@ static bool is_bss(struct cfg80211_bss *a, return memcmp(ssidie + 2, ssid, ssid_len) == 0; } +static bool is_mesh_bss(struct cfg80211_bss *a) +{ + const u8 *ie; + + if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) + return false; + + ie = cfg80211_find_ie(WLAN_EID_MESH_ID, + a->information_elements, + a->len_information_elements); + if (!ie) + return false; + + ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, + a->information_elements, + a->len_information_elements); + if (!ie) + return false; + + return true; +} + static bool is_mesh(struct cfg80211_bss *a, const u8 *meshid, size_t meshidlen, const u8 *meshcfg) { const u8 *ie; - if (!is_zero_ether_addr(a->bssid)) + if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) return false; ie = cfg80211_find_ie(WLAN_EID_MESH_ID, @@ -248,11 +333,7 @@ static int cmp_bss(struct cfg80211_bss *a, if (a->channel != b->channel) return b->channel->center_freq - a->channel->center_freq; - r = memcmp(a->bssid, b->bssid, ETH_ALEN); - if (r) - return r; - - if (is_zero_ether_addr(a->bssid)) { + if (is_mesh_bss(a) && is_mesh_bss(b)) { r = cmp_ies(WLAN_EID_MESH_ID, a->information_elements, a->len_information_elements, @@ -267,6 +348,10 @@ static int cmp_bss(struct cfg80211_bss *a, b->len_information_elements); } + r = memcmp(a->bssid, b->bssid, ETH_ALEN); + if (r) + return r; + return cmp_ies(WLAN_EID_SSID, a->information_elements, a->len_information_elements, @@ -394,7 +479,6 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, struct cfg80211_internal_bss *res) { struct cfg80211_internal_bss *found = NULL; - const u8 *meshid, *meshcfg; /* * The reference to "res" is donated to this function. @@ -407,22 +491,6 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, res->ts = jiffies; - if (is_zero_ether_addr(res->pub.bssid)) { - /* must be mesh, verify */ - meshid = cfg80211_find_ie(WLAN_EID_MESH_ID, - res->pub.information_elements, - res->pub.len_information_elements); - meshcfg = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, - res->pub.information_elements, - res->pub.len_information_elements); - if (!meshid || !meshcfg || - meshcfg[1] != sizeof(struct ieee80211_meshconf_ie)) { - /* bogus mesh */ - kref_put(&res->ref, bss_release); - return NULL; - } - } - spin_lock_bh(&dev->bss_lock); found = rb_find_bss(dev, res); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index e17b0bee6bd..b7b6ff8be55 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -250,7 +250,8 @@ static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev) if (wdev->conn->params.privacy) capa |= WLAN_CAPABILITY_PRIVACY; - bss = cfg80211_get_bss(wdev->wiphy, NULL, wdev->conn->params.bssid, + bss = cfg80211_get_bss(wdev->wiphy, wdev->conn->params.channel, + wdev->conn->params.bssid, wdev->conn->params.ssid, wdev->conn->params.ssid_len, WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY, @@ -470,7 +471,10 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, } if (!bss) - bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, + bss = cfg80211_get_bss(wdev->wiphy, + wdev->conn ? wdev->conn->params.channel : + NULL, + bssid, wdev->ssid, wdev->ssid_len, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); @@ -538,7 +542,9 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, } EXPORT_SYMBOL(cfg80211_connect_result); -void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, +void __cfg80211_roamed(struct wireless_dev *wdev, + struct ieee80211_channel *channel, + const u8 *bssid, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len) { @@ -565,7 +571,7 @@ void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, cfg80211_put_bss(&wdev->current_bss->pub); wdev->current_bss = NULL; - bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, + bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, wdev->ssid, wdev->ssid_len, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); @@ -603,7 +609,9 @@ void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, #endif } -void cfg80211_roamed(struct net_device *dev, const u8 *bssid, +void cfg80211_roamed(struct net_device *dev, + struct ieee80211_channel *channel, + const u8 *bssid, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) { @@ -619,6 +627,7 @@ void cfg80211_roamed(struct net_device *dev, const u8 *bssid, return; ev->type = EVENT_ROAMED; + ev->rm.channel = channel; memcpy(ev->rm.bssid, bssid, ETH_ALEN); ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev); ev->rm.req_ie_len = req_ie_len; diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c index 4294fa22bb2..c6e4ca6a7d2 100644 --- a/net/wireless/sysfs.c +++ b/net/wireless/sysfs.c @@ -93,7 +93,7 @@ static int wiphy_suspend(struct device *dev, pm_message_t state) if (rdev->ops->suspend) { rtnl_lock(); - ret = rdev->ops->suspend(&rdev->wiphy); + ret = rdev->ops->suspend(&rdev->wiphy, rdev->wowlan); rtnl_unlock(); } diff --git a/net/wireless/util.c b/net/wireless/util.c index 6a750bc6bcf..4d7b83fbc32 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -544,7 +544,8 @@ EXPORT_SYMBOL(ieee80211_data_from_8023); void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, const u8 *addr, enum nl80211_iftype iftype, - const unsigned int extra_headroom) + const unsigned int extra_headroom, + bool has_80211_header) { struct sk_buff *frame = NULL; u16 ethertype; @@ -553,14 +554,18 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, int remaining, err; u8 dst[ETH_ALEN], src[ETH_ALEN]; - err = ieee80211_data_to_8023(skb, addr, iftype); - if (err) - goto out; + if (has_80211_header) { + err = ieee80211_data_to_8023(skb, addr, iftype); + if (err) + goto out; - /* skip the wrapping header */ - eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr)); - if (!eth) - goto out; + /* skip the wrapping header */ + eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr)); + if (!eth) + goto out; + } else { + eth = (struct ethhdr *) skb->data; + } while (skb != frame) { u8 padding; @@ -741,7 +746,7 @@ static void cfg80211_process_wdev_events(struct wireless_dev *wdev) NULL); break; case EVENT_ROAMED: - __cfg80211_roamed(wdev, ev->rm.bssid, + __cfg80211_roamed(wdev, ev->rm.channel, ev->rm.bssid, ev->rm.req_ie, ev->rm.req_ie_len, ev->rm.resp_ie, ev->rm.resp_ie_len); break; @@ -803,6 +808,11 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, return -EBUSY; if (ntype != otype) { + err = cfg80211_can_change_interface(rdev, dev->ieee80211_ptr, + ntype); + if (err) + return err; + dev->ieee80211_ptr->use_4addr = false; dev->ieee80211_ptr->mesh_id_up_len = 0; @@ -896,3 +906,103 @@ u16 cfg80211_calculate_bitrate(struct rate_info *rate) /* do NOT round down here */ return (bitrate + 50000) / 100000; } + +int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, + u32 beacon_int) +{ + struct wireless_dev *wdev; + int res = 0; + + if (!beacon_int) + return -EINVAL; + + mutex_lock(&rdev->devlist_mtx); + + list_for_each_entry(wdev, &rdev->netdev_list, list) { + if (!wdev->beacon_interval) + continue; + if (wdev->beacon_interval != beacon_int) { + res = -EINVAL; + break; + } + } + + mutex_unlock(&rdev->devlist_mtx); + + return res; +} + +int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + enum nl80211_iftype iftype) +{ + struct wireless_dev *wdev_iter; + int num[NUM_NL80211_IFTYPES]; + int total = 1; + int i, j; + + ASSERT_RTNL(); + + /* Always allow software iftypes */ + if (rdev->wiphy.software_iftypes & BIT(iftype)) + return 0; + + /* + * Drivers will gradually all set this flag, until all + * have it we only enforce for those that set it. + */ + if (!(rdev->wiphy.flags & WIPHY_FLAG_ENFORCE_COMBINATIONS)) + return 0; + + memset(num, 0, sizeof(num)); + + num[iftype] = 1; + + mutex_lock(&rdev->devlist_mtx); + list_for_each_entry(wdev_iter, &rdev->netdev_list, list) { + if (wdev_iter == wdev) + continue; + if (!netif_running(wdev_iter->netdev)) + continue; + + if (rdev->wiphy.software_iftypes & BIT(wdev_iter->iftype)) + continue; + + num[wdev_iter->iftype]++; + total++; + } + mutex_unlock(&rdev->devlist_mtx); + + for (i = 0; i < rdev->wiphy.n_iface_combinations; i++) { + const struct ieee80211_iface_combination *c; + struct ieee80211_iface_limit *limits; + + c = &rdev->wiphy.iface_combinations[i]; + + limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits, + GFP_KERNEL); + if (!limits) + return -ENOMEM; + if (total > c->max_interfaces) + goto cont; + + for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) { + if (rdev->wiphy.software_iftypes & BIT(iftype)) + continue; + for (j = 0; j < c->n_limits; j++) { + if (!(limits[j].types & iftype)) + continue; + if (limits[j].max < num[iftype]) + goto cont; + limits[j].max -= num[iftype]; + } + } + /* yay, it fits */ + kfree(limits); + return 0; + cont: + kfree(limits); + } + + return -EBUSY; +} diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 15792d8b627..9bec2e8a838 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1348,7 +1348,8 @@ static inline struct xfrm_dst *xfrm_alloc_dst(struct net *net, int family) default: BUG(); } - xdst = dst_alloc(dst_ops, 0); + xdst = dst_alloc(dst_ops, NULL, 0, 0, 0); + memset(&xdst->u.rt6.rt6i_table, 0, sizeof(*xdst) - sizeof(struct dst_entry)); xfrm_policy_put_afinfo(afinfo); if (likely(xdst)) @@ -1406,6 +1407,7 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy, struct net *net = xp_net(policy); unsigned long now = jiffies; struct net_device *dev; + struct xfrm_mode *inner_mode; struct dst_entry *dst_prev = NULL; struct dst_entry *dst0 = NULL; int i = 0; @@ -1436,6 +1438,17 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy, goto put_states; } + if (xfrm[i]->sel.family == AF_UNSPEC) { + inner_mode = xfrm_ip2inner_mode(xfrm[i], + xfrm_af2proto(family)); + if (!inner_mode) { + err = -EAFNOSUPPORT; + dst_release(dst); + goto put_states; + } + } else + inner_mode = xfrm[i]->inner_mode; + if (!dst_prev) dst0 = dst1; else { @@ -1464,7 +1477,7 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy, dst1->lastuse = now; dst1->input = dst_discard; - dst1->output = xfrm[i]->outer_mode->afinfo->output; + dst1->output = inner_mode->afinfo->output; dst1->next = dst_prev; dst_prev = dst1; diff --git a/net/xfrm/xfrm_replay.c b/net/xfrm/xfrm_replay.c index f218385950c..47f1b8638df 100644 --- a/net/xfrm/xfrm_replay.c +++ b/net/xfrm/xfrm_replay.c @@ -532,9 +532,12 @@ int xfrm_init_replay(struct xfrm_state *x) if (replay_esn) { if (replay_esn->replay_window > - replay_esn->bmp_len * sizeof(__u32)) + replay_esn->bmp_len * sizeof(__u32) * 8) return -EINVAL; + if ((x->props.flags & XFRM_STATE_ESN) && replay_esn->replay_window == 0) + return -EINVAL; + if ((x->props.flags & XFRM_STATE_ESN) && x->replay_esn) x->repl = &xfrm_replay_esn; else diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index dd78536d40d..d70f85eb786 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -1036,15 +1036,15 @@ static struct xfrm_state *__find_acq_core(struct net *net, struct xfrm_mark *m, case AF_INET6: ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6, - (struct in6_addr *)daddr); + (const struct in6_addr *)daddr); ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6, - (struct in6_addr *)saddr); + (const struct in6_addr *)saddr); x->sel.prefixlen_d = 128; x->sel.prefixlen_s = 128; ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6, - (struct in6_addr *)saddr); + (const struct in6_addr *)saddr); ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6, - (struct in6_addr *)daddr); + (const struct in6_addr *)daddr); break; } @@ -2092,8 +2092,8 @@ static void xfrm_audit_helper_sainfo(struct xfrm_state *x, static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family, struct audit_buffer *audit_buf) { - struct iphdr *iph4; - struct ipv6hdr *iph6; + const struct iphdr *iph4; + const struct ipv6hdr *iph6; switch (family) { case AF_INET: diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 5d1d60d3ca8..0256b8a0a7c 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -124,6 +124,9 @@ static inline int verify_replay(struct xfrm_usersa_info *p, { struct nlattr *rt = attrs[XFRMA_REPLAY_ESN_VAL]; + if ((p->flags & XFRM_STATE_ESN) && !rt) + return -EINVAL; + if (!rt) return 0; @@ -2296,7 +2299,8 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) if (link->dump == NULL) return -EINVAL; - return netlink_dump_start(net->xfrm.nlsk, skb, nlh, link->dump, link->done); + return netlink_dump_start(net->xfrm.nlsk, skb, nlh, + link->dump, link->done, 0); } err = nlmsg_parse(nlh, xfrm_msg_min[type], attrs, XFRMA_MAX, |