diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/atm/clip.c | 17 | ||||
-rw-r--r-- | net/caif/caif_socket.c | 113 | ||||
-rw-r--r-- | net/caif/chnl_net.c | 12 | ||||
-rw-r--r-- | net/core/dev.c | 94 | ||||
-rw-r--r-- | net/core/neighbour.c | 90 | ||||
-rw-r--r-- | net/core/netpoll.c | 71 | ||||
-rw-r--r-- | net/decnet/dn_neigh.c | 24 | ||||
-rw-r--r-- | net/ipv4/ip_gre.c | 19 | ||||
-rw-r--r-- | net/ipv4/ipip.c | 3 | ||||
-rw-r--r-- | net/ipv4/proc.c | 1 | ||||
-rw-r--r-- | net/ipv4/route.c | 5 | ||||
-rw-r--r-- | net/ipv4/tcp_ipv4.c | 296 | ||||
-rw-r--r-- | net/ipv4/tcp_minisocks.c | 12 | ||||
-rw-r--r-- | net/ipv4/tcp_output.c | 4 | ||||
-rw-r--r-- | net/ipv6/anycast.c | 29 | ||||
-rw-r--r-- | net/ipv6/ip6_fib.c | 19 | ||||
-rw-r--r-- | net/ipv6/ip6_output.c | 10 | ||||
-rw-r--r-- | net/ipv6/ndisc.c | 30 | ||||
-rw-r--r-- | net/ipv6/reassembly.c | 7 | ||||
-rw-r--r-- | net/ipv6/route.c | 15 | ||||
-rw-r--r-- | net/ipv6/sit.c | 20 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 227 | ||||
-rw-r--r-- | net/ipv6/xfrm6_output.c | 2 | ||||
-rw-r--r-- | net/netlink/af_netlink.c | 18 | ||||
-rw-r--r-- | net/netlink/genetlink.c | 31 |
25 files changed, 546 insertions, 623 deletions
diff --git a/net/atm/clip.c b/net/atm/clip.c index c12c2582457c..ef95a30306fa 100644 --- a/net/atm/clip.c +++ b/net/atm/clip.c @@ -328,6 +328,8 @@ static netdev_tx_t clip_start_xmit(struct sk_buff *skb, struct atmarp_entry *entry; struct neighbour *n; struct atm_vcc *vcc; + struct rtable *rt; + __be32 *daddr; int old; unsigned long flags; @@ -338,7 +340,12 @@ static netdev_tx_t clip_start_xmit(struct sk_buff *skb, dev->stats.tx_dropped++; return NETDEV_TX_OK; } - n = dst_get_neighbour_noref(dst); + rt = (struct rtable *) dst; + if (rt->rt_gateway) + daddr = &rt->rt_gateway; + else + daddr = &ip_hdr(skb)->daddr; + n = dst_neigh_lookup(dst, daddr); if (!n) { pr_err("NO NEIGHBOUR !\n"); dev_kfree_skb(skb); @@ -358,7 +365,7 @@ static netdev_tx_t clip_start_xmit(struct sk_buff *skb, dev_kfree_skb(skb); dev->stats.tx_dropped++; } - return NETDEV_TX_OK; + goto out_release_neigh; } pr_debug("neigh %p, vccs %p\n", entry, entry->vccs); ATM_SKB(skb)->vcc = vcc = entry->vccs->vcc; @@ -377,14 +384,14 @@ static netdev_tx_t clip_start_xmit(struct sk_buff *skb, old = xchg(&entry->vccs->xoff, 1); /* assume XOFF ... */ if (old) { pr_warning("XOFF->XOFF transition\n"); - return NETDEV_TX_OK; + goto out_release_neigh; } dev->stats.tx_packets++; dev->stats.tx_bytes += skb->len; vcc->send(vcc, skb); if (atm_may_send(vcc, 0)) { entry->vccs->xoff = 0; - return NETDEV_TX_OK; + goto out_release_neigh; } spin_lock_irqsave(&clip_priv->xoff_lock, flags); netif_stop_queue(dev); /* XOFF -> throttle immediately */ @@ -396,6 +403,8 @@ static netdev_tx_t clip_start_xmit(struct sk_buff *skb, of the brief netif_stop_queue. If this isn't true or if it changes, use netif_wake_queue instead. */ spin_unlock_irqrestore(&clip_priv->xoff_lock, flags); +out_release_neigh: + neigh_release(n); return NETDEV_TX_OK; } diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c index a97d97a3a512..5016fa57b623 100644 --- a/net/caif/caif_socket.c +++ b/net/caif/caif_socket.c @@ -43,34 +43,9 @@ enum caif_states { #define TX_FLOW_ON_BIT 1 #define RX_FLOW_ON_BIT 2 -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; - atomic_t num_disconnect; - atomic_t num_remote_shutdown_ind; - atomic_t num_tx_flow_off_ind; - atomic_t num_tx_flow_on_ind; - atomic_t num_rx_flow_off; - atomic_t num_rx_flow_on; -}; -static struct debug_fs_counter cnt; -#define dbfs_atomic_inc(v) atomic_inc_return(v) -#define dbfs_atomic_dec(v) atomic_dec_return(v) -#else -#define dbfs_atomic_inc(v) 0 -#define dbfs_atomic_dec(v) 0 -#endif - struct caifsock { struct sock sk; /* must be first member */ struct cflayer layer; - char name[CAIF_LAYER_NAME_SZ]; /* Used for debugging */ u32 flow_state; struct caif_connect_request conn_req; struct mutex readlock; @@ -161,7 +136,6 @@ static int caif_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) 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); } @@ -172,7 +146,6 @@ static int caif_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) set_rx_flow_off(cf_sk); 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); } skb->dev = NULL; @@ -233,14 +206,12 @@ static void caif_ctrl_cb(struct cflayer *layr, switch (flow) { case CAIF_CTRLCMD_FLOW_ON_IND: /* OK from modem to start sending again */ - dbfs_atomic_inc(&cnt.num_tx_flow_on_ind); set_tx_flow_on(cf_sk); cf_sk->sk.sk_state_change(&cf_sk->sk); break; case CAIF_CTRLCMD_FLOW_OFF_IND: /* Modem asks us to shut up */ - dbfs_atomic_inc(&cnt.num_tx_flow_off_ind); set_tx_flow_off(cf_sk); cf_sk->sk.sk_state_change(&cf_sk->sk); break; @@ -249,7 +220,6 @@ static void caif_ctrl_cb(struct cflayer *layr, /* 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); cf_sk->sk.sk_state_change(&cf_sk->sk); @@ -263,7 +233,6 @@ static void caif_ctrl_cb(struct cflayer *layr, case CAIF_CTRLCMD_INIT_FAIL_RSP: /* Connect request failed */ - dbfs_atomic_inc(&cnt.num_connect_fail_resp); cf_sk->sk.sk_err = ECONNREFUSED; cf_sk->sk.sk_state = CAIF_DISCONNECTED; cf_sk->sk.sk_shutdown = SHUTDOWN_MASK; @@ -277,7 +246,6 @@ static void caif_ctrl_cb(struct cflayer *layr, case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND: /* Modem has closed this connection, or device is down. */ - dbfs_atomic_inc(&cnt.num_remote_shutdown_ind); cf_sk->sk.sk_shutdown = SHUTDOWN_MASK; cf_sk->sk.sk_err = ECONNRESET; set_rx_flow_on(cf_sk); @@ -297,7 +265,6 @@ static void caif_check_flow_release(struct sock *sk) return; if (atomic_read(&sk->sk_rmem_alloc) <= sk_rcvbuf_lowwater(cf_sk)) { - dbfs_atomic_inc(&cnt.num_rx_flow_on); set_rx_flow_on(cf_sk); caif_flow_ctrl(sk, CAIF_MODEMCMD_FLOW_ON_REQ); } @@ -856,7 +823,6 @@ static int caif_connect(struct socket *sock, struct sockaddr *uaddr, /*ifindex = id of the interface.*/ cf_sk->conn_req.ifindex = cf_sk->sk.sk_bound_dev_if; - dbfs_atomic_inc(&cnt.num_connect_req); cf_sk->layer.receive = caif_sktrecv_cb; err = caif_connect_client(sock_net(sk), &cf_sk->conn_req, @@ -945,8 +911,6 @@ static int caif_release(struct socket *sock) 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); @@ -1054,14 +1018,12 @@ static void caif_sock_destructor(struct sock *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", @@ -1122,34 +1084,6 @@ static int caif_create(struct net *net, struct socket *sock, int protocol, 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", 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); - debugfs_create_u32("flow_state", S_IRUSR | S_IWUSR, - cf_sk->debugfs_socket_dir, &cf_sk->flow_state); - debugfs_create_u32("sk_rmem_alloc", S_IRUSR | S_IWUSR, - cf_sk->debugfs_socket_dir, - (u32 *) &cf_sk->sk.sk_rmem_alloc); - debugfs_create_u32("sk_wmem_alloc", S_IRUSR | S_IWUSR, - cf_sk->debugfs_socket_dir, - (u32 *) &cf_sk->sk.sk_wmem_alloc); - debugfs_create_u32("identity", S_IRUSR | S_IWUSR, - cf_sk->debugfs_socket_dir, - (u32 *) &cf_sk->layer.id); - } -#endif release_sock(&cf_sk->sk); return 0; } @@ -1161,7 +1095,7 @@ static struct net_proto_family caif_family_ops = { .owner = THIS_MODULE, }; -static int af_caif_init(void) +static int __init caif_sktinit_module(void) { int err = sock_register(&caif_family_ops); if (!err) @@ -1169,54 +1103,9 @@ static int af_caif_init(void) return 0; } -static int __init caif_sktinit_module(void) -{ -#ifdef CONFIG_DEBUG_FS - debugfsdir = debugfs_create_dir("caif_sk", NULL); - if (!IS_ERR(debugfsdir)) { - 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); - debugfs_create_u32("num_connect_resp", S_IRUSR | S_IWUSR, - debugfsdir, - (u32 *) &cnt.num_connect_resp); - debugfs_create_u32("num_connect_fail_resp", S_IRUSR | S_IWUSR, - debugfsdir, - (u32 *) &cnt.num_connect_fail_resp); - debugfs_create_u32("num_disconnect", S_IRUSR | S_IWUSR, - debugfsdir, - (u32 *) &cnt.num_disconnect); - debugfs_create_u32("num_remote_shutdown_ind", - S_IRUSR | S_IWUSR, debugfsdir, - (u32 *) &cnt.num_remote_shutdown_ind); - debugfs_create_u32("num_tx_flow_off_ind", S_IRUSR | S_IWUSR, - debugfsdir, - (u32 *) &cnt.num_tx_flow_off_ind); - debugfs_create_u32("num_tx_flow_on_ind", S_IRUSR | S_IWUSR, - debugfsdir, - (u32 *) &cnt.num_tx_flow_on_ind); - debugfs_create_u32("num_rx_flow_off", S_IRUSR | S_IWUSR, - debugfsdir, - (u32 *) &cnt.num_rx_flow_off); - debugfs_create_u32("num_rx_flow_on", S_IRUSR | S_IWUSR, - debugfsdir, - (u32 *) &cnt.num_rx_flow_on); - } -#endif - return af_caif_init(); -} - static void __exit caif_sktexit_module(void) { sock_unregister(PF_CAIF); - if (debugfsdir != NULL) - debugfs_remove_recursive(debugfsdir); } module_init(caif_sktinit_module); module_exit(caif_sktexit_module); diff --git a/net/caif/chnl_net.c b/net/caif/chnl_net.c index 865690948bbc..a751d9b263ed 100644 --- a/net/caif/chnl_net.c +++ b/net/caif/chnl_net.c @@ -74,7 +74,6 @@ static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt) struct sk_buff *skb; struct chnl_net *priv = container_of(layr, struct chnl_net, chnl); int pktlen; - int err = 0; const u8 *ip_version; u8 buf; @@ -95,8 +94,7 @@ static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt) /* check the version of IP */ ip_version = skb_header_pointer(skb, 0, 1, &buf); - if (!ip_version) - return -EINVAL; + switch (*ip_version >> 4) { case 4: skb->protocol = htons(ETH_P_IP); @@ -105,6 +103,7 @@ static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt) skb->protocol = htons(ETH_P_IPV6); break; default: + priv->netdev->stats.rx_errors++; return -EINVAL; } @@ -123,7 +122,7 @@ static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt) priv->netdev->stats.rx_packets++; priv->netdev->stats.rx_bytes += pktlen; - return err; + return 0; } static int delete_device(struct chnl_net *dev) @@ -221,11 +220,13 @@ static int chnl_net_start_xmit(struct sk_buff *skb, struct net_device *dev) if (skb->len > priv->netdev->mtu) { pr_warn("Size of skb exceeded MTU\n"); + dev->stats.tx_errors++; return -ENOSPC; } if (!priv->flowenabled) { pr_debug("dropping packets flow off\n"); + dev->stats.tx_dropped++; return NETDEV_TX_BUSY; } @@ -240,8 +241,7 @@ static int chnl_net_start_xmit(struct sk_buff *skb, struct net_device *dev) /* Send the packet down the stack. */ result = priv->chnl.dn->transmit(priv->chnl.dn, pkt); if (result) { - if (result == -EAGAIN) - result = NETDEV_TX_BUSY; + dev->stats.tx_dropped++; return result; } diff --git a/net/core/dev.c b/net/core/dev.c index 115dee1d985d..f1249472e90e 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -446,7 +446,7 @@ void __dev_remove_pack(struct packet_type *pt) } } - printk(KERN_WARNING "dev_remove_pack: %p not found.\n", pt); + pr_warn("dev_remove_pack: %p not found\n", pt); out: spin_unlock(&ptype_lock); } @@ -1039,8 +1039,7 @@ rollback: memcpy(dev->name, oldname, IFNAMSIZ); goto rollback; } else { - printk(KERN_ERR - "%s: name change rollback failed: %d.\n", + pr_err("%s: name change rollback failed: %d\n", dev->name, ret); } } @@ -1139,9 +1138,8 @@ void dev_load(struct net *net, const char *name) no_module = request_module("netdev-%s", name); if (no_module && capable(CAP_SYS_MODULE)) { if (!request_module("%s", name)) - pr_err("Loading kernel module for a network device " -"with CAP_SYS_MODULE (deprecated). Use CAP_NET_ADMIN and alias netdev-%s " -"instead\n", name); + pr_err("Loading kernel module for a network device with CAP_SYS_MODULE (deprecated). Use CAP_NET_ADMIN and alias netdev-%s instead.\n", + name); } } EXPORT_SYMBOL(dev_load); @@ -1655,10 +1653,9 @@ static void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev) if (skb_network_header(skb2) < skb2->data || skb2->network_header > skb2->tail) { if (net_ratelimit()) - printk(KERN_CRIT "protocol %04x is " - "buggy, dev %s\n", - ntohs(skb2->protocol), - dev->name); + pr_crit("protocol %04x is buggy, dev %s\n", + ntohs(skb2->protocol), + dev->name); skb_reset_network_header(skb2); } @@ -1691,9 +1688,7 @@ static void netif_setup_tc(struct net_device *dev, unsigned int txq) /* If TC0 is invalidated disable TC mapping */ if (tc->offset + tc->count > txq) { - pr_warning("Number of in use tx queues changed " - "invalidating tc mappings. Priority " - "traffic classification disabled!\n"); + pr_warn("Number of in use tx queues changed invalidating tc mappings. Priority traffic classification disabled!\n"); dev->num_tc = 0; return; } @@ -1704,11 +1699,8 @@ static void netif_setup_tc(struct net_device *dev, unsigned int txq) tc = &dev->tc_to_txq[q]; if (tc->offset + tc->count > txq) { - pr_warning("Number of in use tx queues " - "changed. Priority %i to tc " - "mapping %i is no longer valid " - "setting map to 0\n", - i, q); + pr_warn("Number of in use tx queues changed. Priority %i to tc mapping %i is no longer valid. Setting map to 0\n", + i, q); netdev_set_prio_tc_map(dev, i, 0); } } @@ -2014,8 +2006,7 @@ EXPORT_SYMBOL(skb_gso_segment); void netdev_rx_csum_fault(struct net_device *dev) { if (net_ratelimit()) { - printk(KERN_ERR "%s: hw csum failure.\n", - dev ? dev->name : "<unknown>"); + pr_err("%s: hw csum failure\n", dev ? dev->name : "<unknown>"); dump_stack(); } } @@ -2332,9 +2323,9 @@ static inline u16 dev_cap_txqueue(struct net_device *dev, u16 queue_index) { if (unlikely(queue_index >= dev->real_num_tx_queues)) { if (net_ratelimit()) { - pr_warning("%s selects TX queue %d, but " - "real number of TX queues is %d\n", - dev->name, queue_index, dev->real_num_tx_queues); + pr_warn("%s selects TX queue %d, but real number of TX queues is %d\n", + dev->name, queue_index, + dev->real_num_tx_queues); } return 0; } @@ -2578,16 +2569,16 @@ int dev_queue_xmit(struct sk_buff *skb) } HARD_TX_UNLOCK(dev, txq); if (net_ratelimit()) - printk(KERN_CRIT "Virtual device %s asks to " - "queue packet!\n", dev->name); + pr_crit("Virtual device %s asks to queue packet!\n", + dev->name); } else { /* Recursion is detected! It is possible, * unfortunately */ recursion_alert: if (net_ratelimit()) - printk(KERN_CRIT "Dead loop on virtual device " - "%s, fix it urgently!\n", dev->name); + pr_crit("Dead loop on virtual device %s, fix it urgently!\n", + dev->name); } } @@ -3069,8 +3060,8 @@ static int ing_filter(struct sk_buff *skb, struct netdev_queue *rxq) if (unlikely(MAX_RED_LOOP < ttl++)) { if (net_ratelimit()) - pr_warning( "Redir loop detected Dropping packet (%d->%d)\n", - skb->skb_iif, dev->ifindex); + pr_warn("Redir loop detected Dropping packet (%d->%d)\n", + skb->skb_iif, dev->ifindex); return TC_ACT_SHOT; } @@ -4491,16 +4482,15 @@ static int __dev_set_promiscuity(struct net_device *dev, int inc) dev->flags &= ~IFF_PROMISC; else { dev->promiscuity -= inc; - printk(KERN_WARNING "%s: promiscuity touches roof, " - "set promiscuity failed, promiscuity feature " - "of device might be broken.\n", dev->name); + pr_warn("%s: promiscuity touches roof, set promiscuity failed. promiscuity feature of device might be broken.\n", + dev->name); return -EOVERFLOW; } } if (dev->flags != old_flags) { - printk(KERN_INFO "device %s %s promiscuous mode\n", - dev->name, (dev->flags & IFF_PROMISC) ? "entered" : - "left"); + pr_info("device %s %s promiscuous mode\n", + dev->name, + dev->flags & IFF_PROMISC ? "entered" : "left"); if (audit_enabled) { current_uid_gid(&uid, &gid); audit_log(current->audit_context, GFP_ATOMIC, @@ -4573,9 +4563,8 @@ int dev_set_allmulti(struct net_device *dev, int inc) dev->flags &= ~IFF_ALLMULTI; else { dev->allmulti -= inc; - printk(KERN_WARNING "%s: allmulti touches roof, " - "set allmulti failed, allmulti feature of " - "device might be broken.\n", dev->name); + pr_warn("%s: allmulti touches roof, set allmulti failed. allmulti feature of device might be broken.\n", + dev->name); return -EOVERFLOW; } } @@ -5232,8 +5221,8 @@ static void rollback_registered_many(struct list_head *head) * devices and proceed with the remaining. */ if (dev->reg_state == NETREG_UNINITIALIZED) { - pr_debug("unregister_netdevice: device %s/%p never " - "was registered\n", dev->name, dev); + pr_debug("unregister_netdevice: device %s/%p never was registered\n", + dev->name, dev); WARN_ON(1); list_del(&dev->unreg_list); @@ -5465,7 +5454,7 @@ static int netif_alloc_rx_queues(struct net_device *dev) rx = kcalloc(count, sizeof(struct netdev_rx_queue), GFP_KERNEL); if (!rx) { - pr_err("netdev: Unable to allocate %u rx queues.\n", count); + pr_err("netdev: Unable to allocate %u rx queues\n", count); return -ENOMEM; } dev->_rx = rx; @@ -5499,8 +5488,7 @@ static int netif_alloc_netdev_queues(struct net_device *dev) tx = kcalloc(count, sizeof(struct netdev_queue), GFP_KERNEL); if (!tx) { - pr_err("netdev: Unable to allocate %u tx queues.\n", - count); + pr_err("netdev: Unable to allocate %u tx queues\n", count); return -ENOMEM; } dev->_tx = tx; @@ -5759,10 +5747,8 @@ static void netdev_wait_allrefs(struct net_device *dev) refcnt = netdev_refcnt_read(dev); if (time_after(jiffies, warning_time + 10 * HZ)) { - printk(KERN_EMERG "unregister_netdevice: " - "waiting for %s to become free. Usage " - "count = %d\n", - dev->name, refcnt); + pr_emerg("unregister_netdevice: waiting for %s to become free. Usage count = %d\n", + dev->name, refcnt); warning_time = jiffies; } } @@ -5813,7 +5799,7 @@ void netdev_run_todo(void) list_del(&dev->todo_list); if (unlikely(dev->reg_state != NETREG_UNREGISTERING)) { - printk(KERN_ERR "network todo '%s' but state %d\n", + pr_err("network todo '%s' but state %d\n", dev->name, dev->reg_state); dump_stack(); continue; @@ -5929,15 +5915,13 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, BUG_ON(strlen(name) >= sizeof(dev->name)); if (txqs < 1) { - pr_err("alloc_netdev: Unable to allocate device " - "with zero queues.\n"); + pr_err("alloc_netdev: Unable to allocate device with zero queues\n"); return NULL; } #ifdef CONFIG_RPS if (rxqs < 1) { - pr_err("alloc_netdev: Unable to allocate device " - "with zero RX queues.\n"); + pr_err("alloc_netdev: Unable to allocate device with zero RX queues\n"); return NULL; } #endif @@ -5953,7 +5937,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, p = kzalloc(alloc_size, GFP_KERNEL); if (!p) { - printk(KERN_ERR "alloc_netdev: Unable to allocate device.\n"); + pr_err("alloc_netdev: Unable to allocate device\n"); return NULL; } @@ -6486,8 +6470,8 @@ static void __net_exit default_device_exit(struct net *net) snprintf(fb_name, IFNAMSIZ, "dev%d", dev->ifindex); err = dev_change_net_namespace(dev, &init_net, fb_name); if (err) { - printk(KERN_EMERG "%s: failed to move %s to init_net: %d\n", - __func__, dev->name, err); + pr_emerg("%s: failed to move %s to init_net: %d\n", + __func__, dev->name, err); BUG(); } } diff --git a/net/core/neighbour.c b/net/core/neighbour.c index e287346e0934..f98ec444133a 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -2165,6 +2165,35 @@ nla_put_failure: return -EMSGSIZE; } +static int pneigh_fill_info(struct sk_buff *skb, struct pneigh_entry *pn, + u32 pid, u32 seq, int type, unsigned int flags, + struct neigh_table *tbl) +{ + struct nlmsghdr *nlh; + struct ndmsg *ndm; + + nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndm), flags); + if (nlh == NULL) + return -EMSGSIZE; + + ndm = nlmsg_data(nlh); + ndm->ndm_family = tbl->family; + ndm->ndm_pad1 = 0; + ndm->ndm_pad2 = 0; + ndm->ndm_flags = pn->flags | NTF_PROXY; + ndm->ndm_type = NDA_DST; + ndm->ndm_ifindex = pn->dev->ifindex; + ndm->ndm_state = NUD_NONE; + + NLA_PUT(skb, NDA_DST, tbl->key_len, pn->key); + + return nlmsg_end(skb, nlh); + +nla_put_failure: + nlmsg_cancel(skb, nlh); + return -EMSGSIZE; +} + static void neigh_update_notify(struct neighbour *neigh) { call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, neigh); @@ -2214,23 +2243,78 @@ out: return rc; } +static int pneigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct pneigh_entry *n; + struct net *net = sock_net(skb->sk); + int rc, h, s_h = cb->args[3]; + int idx, s_idx = idx = cb->args[4]; + + read_lock_bh(&tbl->lock); + + for (h = 0; h <= PNEIGH_HASHMASK; h++) { + if (h < s_h) + continue; + if (h > s_h) + s_idx = 0; + for (n = tbl->phash_buckets[h], idx = 0; n; n = n->next) { + if (dev_net(n->dev) != net) + continue; + if (idx < s_idx) + goto next; + if (pneigh_fill_info(skb, n, NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, + RTM_NEWNEIGH, + NLM_F_MULTI, tbl) <= 0) { + read_unlock_bh(&tbl->lock); + rc = -1; + goto out; + } + next: + idx++; + } + } + + read_unlock_bh(&tbl->lock); + rc = skb->len; +out: + cb->args[3] = h; + cb->args[4] = idx; + return rc; + +} + static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb) { struct neigh_table *tbl; int t, family, s_t; + int proxy = 0; + int err = 0; read_lock(&neigh_tbl_lock); family = ((struct rtgenmsg *) nlmsg_data(cb->nlh))->rtgen_family; + + /* check for full ndmsg structure presence, family member is + * the same for both structures + */ + if (nlmsg_len(cb->nlh) >= sizeof(struct ndmsg) && + ((struct ndmsg *) nlmsg_data(cb->nlh))->ndm_flags == NTF_PROXY) + proxy = 1; + s_t = cb->args[0]; - for (tbl = neigh_tables, t = 0; tbl; tbl = tbl->next, t++) { + for (tbl = neigh_tables, t = 0; tbl && (err >= 0); + tbl = tbl->next, t++) { if (t < s_t || (family && tbl->family != family)) continue; if (t > s_t) memset(&cb->args[1], 0, sizeof(cb->args) - sizeof(cb->args[0])); - if (neigh_dump_table(tbl, skb, cb) < 0) - break; + if (proxy) + err = pneigh_dump_table(tbl, skb, cb); + else + err = neigh_dump_table(tbl, skb, cb); } read_unlock(&neigh_tbl_lock); diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 556b08298669..4ce473ea5dc0 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -9,6 +9,8 @@ * Copyright (C) 2002 Red Hat, Inc. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/moduleparam.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> @@ -45,9 +47,11 @@ static atomic_t trapped; #define NETPOLL_RX_ENABLED 1 #define NETPOLL_RX_DROP 2 -#define MAX_SKB_SIZE \ - (MAX_UDP_CHUNK + sizeof(struct udphdr) + \ - sizeof(struct iphdr) + sizeof(struct ethhdr)) +#define MAX_SKB_SIZE \ + (sizeof(struct ethhdr) + \ + sizeof(struct iphdr) + \ + sizeof(struct udphdr) + \ + MAX_UDP_CHUNK) static void zap_completion_queue(void); static void arp_reply(struct sk_buff *skb); @@ -55,6 +59,13 @@ static void arp_reply(struct sk_buff *skb); static unsigned int carrier_timeout = 4; module_param(carrier_timeout, uint, 0644); +#define np_info(np, fmt, ...) \ + pr_info("%s: " fmt, np->name, ##__VA_ARGS__) +#define np_err(np, fmt, ...) \ + pr_err("%s: " fmt, np->name, ##__VA_ARGS__) +#define np_notice(np, fmt, ...) \ + pr_notice("%s: " fmt, np->name, ##__VA_ARGS__) + static void queue_process(struct work_struct *work) { struct netpoll_info *npinfo = @@ -627,18 +638,12 @@ out: void netpoll_print_options(struct netpoll *np) { - printk(KERN_INFO "%s: local port %d\n", - np->name, np->local_port); - printk(KERN_INFO "%s: local IP %pI4\n", - np->name, &np->local_ip); - printk(KERN_INFO "%s: interface '%s'\n", - np->name, np->dev_name); - printk(KERN_INFO "%s: remote port %d\n", - np->name, np->remote_port); - printk(KERN_INFO "%s: remote IP %pI4\n", - np->name, &np->remote_ip); - printk(KERN_INFO "%s: remote ethernet address %pM\n", - np->name, np->remote_mac); + np_info(np, "local port %d\n", np->local_port); + np_info(np, "local IP %pI4\n", &np->local_ip); + np_info(np, "interface '%s'\n", np->dev_name); + np_info(np, "remote port %d\n", np->remote_port); + np_info(np, "remote IP %pI4\n", &np->remote_ip); + np_info(np, "remote ethernet address %pM\n", np->remote_mac); } EXPORT_SYMBOL(netpoll_print_options); @@ -680,8 +685,7 @@ int netpoll_parse_options(struct netpoll *np, char *opt) goto parse_failed; *delim = 0; if (*cur == ' ' || *cur == '\t') - printk(KERN_INFO "%s: warning: whitespace" - "is not allowed\n", np->name); + np_info(np, "warning: whitespace is not allowed\n"); np->remote_port = simple_strtol(cur, NULL, 10); cur = delim; } @@ -705,8 +709,7 @@ int netpoll_parse_options(struct netpoll *np, char *opt) return 0; parse_failed: - printk(KERN_INFO "%s: couldn't parse config at '%s'!\n", - np->name, cur); + np_info(np, "couldn't parse config at '%s'!\n", cur); return -1; } EXPORT_SYMBOL(netpoll_parse_options); @@ -721,8 +724,8 @@ int __netpoll_setup(struct netpoll *np) if ((ndev->priv_flags & IFF_DISABLE_NETPOLL) || !ndev->netdev_ops->ndo_poll_controller) { - printk(KERN_ERR "%s: %s doesn't support polling, aborting.\n", - np->name, np->dev_name); + np_err(np, "%s doesn't support polling, aborting\n", + np->dev_name); err = -ENOTSUPP; goto out; } @@ -785,14 +788,12 @@ int netpoll_setup(struct netpoll *np) if (np->dev_name) ndev = dev_get_by_name(&init_net, np->dev_name); if (!ndev) { - printk(KERN_ERR "%s: %s doesn't exist, aborting.\n", - np->name, np->dev_name); + np_err(np, "%s doesn't exist, aborting\n", np->dev_name); return -ENODEV; } if (ndev->master) { - printk(KERN_ERR "%s: %s is a slave device, aborting.\n", - np->name, np->dev_name); + np_err(np, "%s is a slave device, aborting\n", np->dev_name); err = -EBUSY; goto put; } @@ -800,16 +801,14 @@ int netpoll_setup(struct netpoll *np) if (!netif_running(ndev)) { unsigned long atmost, atleast; - printk(KERN_INFO "%s: device %s not up yet, forcing it\n", - np->name, np->dev_name); + np_info(np, "device %s not up yet, forcing it\n", np->dev_name); rtnl_lock(); err = dev_open(ndev); rtnl_unlock(); if (err) { - printk(KERN_ERR "%s: failed to open %s\n", - np->name, ndev->name); + np_err(np, "failed to open %s\n", ndev->name); goto put; } @@ -817,9 +816,7 @@ int netpoll_setup(struct netpoll *np) atmost = jiffies + carrier_timeout * HZ; while (!netif_carrier_ok(ndev)) { if (time_after(jiffies, atmost)) { - printk(KERN_NOTICE - "%s: timeout waiting for carrier\n", - np->name); + np_notice(np, "timeout waiting for carrier\n"); break; } msleep(1); @@ -831,9 +828,7 @@ int netpoll_setup(struct netpoll *np) */ if (time_before(jiffies, atleast)) { - printk(KERN_NOTICE "%s: carrier detect appears" - " untrustworthy, waiting 4 seconds\n", - np->name); + np_notice(np, "carrier detect appears untrustworthy, waiting 4 seconds\n"); msleep(4000); } } @@ -844,15 +839,15 @@ int netpoll_setup(struct netpoll *np) if (!in_dev || !in_dev->ifa_list) { rcu_read_unlock(); - printk(KERN_ERR "%s: no IP address for %s, aborting\n", - np->name, np->dev_name); + np_err(np, "no IP address for %s, aborting\n", + np->dev_name); err = -EDESTADDRREQ; goto put; } np->local_ip = in_dev->ifa_list->ifa_local; rcu_read_unlock(); - printk(KERN_INFO "%s: local IP %pI4\n", np->name, &np->local_ip); + np_info(np, "local IP %pI4\n", &np->local_ip); } np->dev = ndev; diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c index befe426491ba..ee7013f24fca 100644 --- a/net/decnet/dn_neigh.c +++ b/net/decnet/dn_neigh.c @@ -205,17 +205,23 @@ static int dn_neigh_output_packet(struct sk_buff *skb) struct neighbour *neigh = dst_get_neighbour_noref(dst); struct net_device *dev = neigh->dev; char mac_addr[ETH_ALEN]; + unsigned int seq; + int err; dn_dn2eth(mac_addr, rt->rt_local_src); - if (dev_hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, - mac_addr, skb->len) >= 0) - return dev_queue_xmit(skb); - - if (net_ratelimit()) - printk(KERN_DEBUG "dn_neigh_output_packet: oops, can't send packet\n"); - - kfree_skb(skb); - return -EINVAL; + do { + seq = read_seqbegin(&neigh->ha_lock); + err = dev_hard_header(skb, dev, ntohs(skb->protocol), + neigh->ha, mac_addr, skb->len); + } while (read_seqretry(&neigh->ha_lock, seq)); + + if (err >= 0) + err = dev_queue_xmit(skb); + else { + kfree_skb(skb); + err = -EINVAL; + } + return err; } static int dn_long_output(struct neighbour *neigh, struct sk_buff *skb) diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 6b3ca5ba4450..0286d78c589c 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -730,15 +730,16 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev if (skb->protocol == htons(ETH_P_IP)) { rt = skb_rtable(skb); - if ((dst = rt->rt_gateway) == 0) - goto tx_error_icmp; + dst = rt->rt_gateway; } #if IS_ENABLED(CONFIG_IPV6) else if (skb->protocol == htons(ETH_P_IPV6)) { - struct neighbour *neigh = dst_get_neighbour_noref(skb_dst(skb)); const struct in6_addr *addr6; + struct neighbour *neigh; + bool do_tx_error_icmp; int addr_type; + neigh = dst_neigh_lookup(skb_dst(skb), &ipv6_hdr(skb)->daddr); if (neigh == NULL) goto tx_error; @@ -751,9 +752,14 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev } if ((addr_type & IPV6_ADDR_COMPATv4) == 0) + do_tx_error_icmp = true; + else { + do_tx_error_icmp = false; + dst = addr6->s6_addr32[3]; + } + neigh_release(neigh); + if (do_tx_error_icmp) goto tx_error_icmp; - - dst = addr6->s6_addr32[3]; } #endif else @@ -914,9 +920,10 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev __IPTUNNEL_XMIT(tstats, &dev->stats); return NETDEV_TX_OK; +#if IS_ENABLED(CONFIG_IPV6) tx_error_icmp: dst_link_failure(skb); - +#endif tx_error: dev->stats.tx_errors++; dev_kfree_skb(skb); diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 22a199315309..f84ebff5cdb0 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -454,8 +454,7 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) dev->stats.tx_fifo_errors++; goto tx_error; } - if ((dst = rt->rt_gateway) == 0) - goto tx_error_icmp; + dst = rt->rt_gateway; } rt = ip_route_output_ports(dev_net(dev), &fl4, NULL, diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 6afc807ee2ad..02d61079f08b 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -256,6 +256,7 @@ static const struct snmp_mib snmp4_net_list[] = { SNMP_MIB_ITEM("TCPTimeWaitOverflow", LINUX_MIB_TCPTIMEWAITOVERFLOW), SNMP_MIB_ITEM("TCPReqQFullDoCookies", LINUX_MIB_TCPREQQFULLDOCOOKIES), SNMP_MIB_ITEM("TCPReqQFullDrop", LINUX_MIB_TCPREQQFULLDROP), + SNMP_MIB_ITEM("TCPRetransFail", LINUX_MIB_TCPRETRANSFAIL), SNMP_MIB_SENTINEL }; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index bcacf54e5418..4eeb8ce856e2 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1117,10 +1117,15 @@ static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst, const vo static const __be32 inaddr_any = 0; struct net_device *dev = dst->dev; const __be32 *pkey = daddr; + const struct rtable *rt; struct neighbour *n; + rt = (const struct rtable *) dst; + if (dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) pkey = &inaddr_any; + else if (rt->rt_gateway) + pkey = (const __be32 *) &rt->rt_gateway; n = __ipv4_neigh_lookup(&arp_tbl, dev, *(__force u32 *)pkey); if (n) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 337ba4cca052..90e47931e217 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -90,16 +90,8 @@ EXPORT_SYMBOL(sysctl_tcp_low_latency); #ifdef CONFIG_TCP_MD5SIG -static struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk, - __be32 addr); -static int tcp_v4_md5_hash_hdr(char *md5_hash, struct tcp_md5sig_key *key, +static int tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key, __be32 daddr, __be32 saddr, const struct tcphdr *th); -#else -static inline -struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk, __be32 addr) -{ - return NULL; -} #endif struct inet_hashinfo tcp_hashinfo; @@ -601,6 +593,10 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) struct ip_reply_arg arg; #ifdef CONFIG_TCP_MD5SIG struct tcp_md5sig_key *key; + const __u8 *hash_location = NULL; + unsigned char newhash[16]; + int genhash; + struct sock *sk1 = NULL; #endif struct net *net; @@ -631,7 +627,36 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) arg.iov[0].iov_len = sizeof(rep.th); #ifdef CONFIG_TCP_MD5SIG - key = sk ? tcp_v4_md5_do_lookup(sk, ip_hdr(skb)->saddr) : NULL; + hash_location = tcp_parse_md5sig_option(th); + if (!sk && hash_location) { + /* + * active side is lost. Try to find listening socket through + * source port, and then find md5 key through listening socket. + * we are not loose security here: + * Incoming packet is checked with md5 hash with finding key, + * no RST generated if md5 hash doesn't match. + */ + sk1 = __inet_lookup_listener(dev_net(skb_dst(skb)->dev), + &tcp_hashinfo, ip_hdr(skb)->daddr, + ntohs(th->source), inet_iif(skb)); + /* don't send rst if it can't find key */ + if (!sk1) + return; + rcu_read_lock(); + key = tcp_md5_do_lookup(sk1, (union tcp_md5_addr *) + &ip_hdr(skb)->saddr, AF_INET); + if (!key) + goto release_sk1; + + genhash = tcp_v4_md5_hash_skb(newhash, key, NULL, NULL, skb); + if (genhash || memcmp(hash_location, newhash, 16) != 0) + goto release_sk1; + } else { + key = sk ? tcp_md5_do_lookup(sk, (union tcp_md5_addr *) + &ip_hdr(skb)->saddr, + AF_INET) : NULL; + } + if (key) { rep.opt[0] = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | @@ -659,6 +684,14 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) TCP_INC_STATS_BH(net, TCP_MIB_OUTSEGS); TCP_INC_STATS_BH(net, TCP_MIB_OUTRSTS); + +#ifdef CONFIG_TCP_MD5SIG +release_sk1: + if (sk1) { + rcu_read_unlock(); + sock_put(sk1); + } +#endif } /* The code following below sending ACKs in SYN-RECV and TIME-WAIT states @@ -759,7 +792,8 @@ static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, tcp_rsk(req)->rcv_isn + 1, req->rcv_wnd, req->ts_recent, 0, - tcp_v4_md5_do_lookup(sk, ip_hdr(skb)->daddr), + tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&ip_hdr(skb)->daddr, + AF_INET), inet_rsk(req)->no_srccheck ? IP_REPLY_ARG_NOSRCCHECK : 0, ip_hdr(skb)->tos); } @@ -876,153 +910,137 @@ static struct ip_options_rcu *tcp_v4_save_options(struct sock *sk, */ /* Find the Key structure for an address. */ -static struct tcp_md5sig_key * - tcp_v4_md5_do_lookup(struct sock *sk, __be32 addr) +struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk, + const union tcp_md5_addr *addr, + int family) { struct tcp_sock *tp = tcp_sk(sk); - int i; - - if (!tp->md5sig_info || !tp->md5sig_info->entries4) + struct tcp_md5sig_key *key; + struct hlist_node *pos; + unsigned int size = sizeof(struct in_addr); + struct tcp_md5sig_info *md5sig; + + /* caller either holds rcu_read_lock() or socket lock */ + md5sig = rcu_dereference_check(tp->md5sig_info, + sock_owned_by_user(sk)); + if (!md5sig) return NULL; - for (i = 0; i < tp->md5sig_info->entries4; i++) { - if (tp->md5sig_info->keys4[i].addr == addr) - return &tp->md5sig_info->keys4[i].base; +#if IS_ENABLED(CONFIG_IPV6) + if (family == AF_INET6) + size = sizeof(struct in6_addr); +#endif + hlist_for_each_entry_rcu(key, pos, &md5sig->head, node) { + if (key->family != family) + continue; + if (!memcmp(&key->addr, addr, size)) + return key; } return NULL; } +EXPORT_SYMBOL(tcp_md5_do_lookup); struct tcp_md5sig_key *tcp_v4_md5_lookup(struct sock *sk, struct sock *addr_sk) { - return tcp_v4_md5_do_lookup(sk, inet_sk(addr_sk)->inet_daddr); + union tcp_md5_addr *addr; + + addr = (union tcp_md5_addr *)&inet_sk(addr_sk)->inet_daddr; + return tcp_md5_do_lookup(sk, addr, AF_INET); } EXPORT_SYMBOL(tcp_v4_md5_lookup); static struct tcp_md5sig_key *tcp_v4_reqsk_md5_lookup(struct sock *sk, struct request_sock *req) { - return tcp_v4_md5_do_lookup(sk, inet_rsk(req)->rmt_addr); + union tcp_md5_addr *addr; + + addr = (union tcp_md5_addr *)&inet_rsk(req)->rmt_addr; + return tcp_md5_do_lookup(sk, addr, AF_INET); } /* This can be called on a newly created socket, from other files */ -int tcp_v4_md5_do_add(struct sock *sk, __be32 addr, - u8 *newkey, u8 newkeylen) +int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, + int family, const u8 *newkey, u8 newkeylen, gfp_t gfp) { /* Add Key to the list */ struct tcp_md5sig_key *key; struct tcp_sock *tp = tcp_sk(sk); - struct tcp4_md5sig_key *keys; + struct tcp_md5sig_info *md5sig; - key = tcp_v4_md5_do_lookup(sk, addr); + key = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&addr, AF_INET); if (key) { /* Pre-existing entry - just update that one. */ - kfree(key->key); - key->key = newkey; + memcpy(key->key, newkey, newkeylen); key->keylen = newkeylen; - } else { - struct tcp_md5sig_info *md5sig; - - if (!tp->md5sig_info) { - tp->md5sig_info = kzalloc(sizeof(*tp->md5sig_info), - GFP_ATOMIC); - if (!tp->md5sig_info) { - kfree(newkey); - return -ENOMEM; - } - sk_nocaps_add(sk, NETIF_F_GSO_MASK); - } + return 0; + } - md5sig = tp->md5sig_info; - if (md5sig->entries4 == 0 && - tcp_alloc_md5sig_pool(sk) == NULL) { - kfree(newkey); + md5sig = rcu_dereference_protected(tp->md5sig_info, + sock_owned_by_user(sk)); + if (!md5sig) { + md5sig = kmalloc(sizeof(*md5sig), gfp); + if (!md5sig) return -ENOMEM; - } - - if (md5sig->alloced4 == md5sig->entries4) { - keys = kmalloc((sizeof(*keys) * - (md5sig->entries4 + 1)), GFP_ATOMIC); - if (!keys) { - kfree(newkey); - if (md5sig->entries4 == 0) - tcp_free_md5sig_pool(); - return -ENOMEM; - } - if (md5sig->entries4) - memcpy(keys, md5sig->keys4, - sizeof(*keys) * md5sig->entries4); + sk_nocaps_add(sk, NETIF_F_GSO_MASK); + INIT_HLIST_HEAD(&md5sig->head); + rcu_assign_pointer(tp->md5sig_info, md5sig); + } - /* Free old key list, and reference new one */ - kfree(md5sig->keys4); - md5sig->keys4 = keys; - md5sig->alloced4++; - } - md5sig->entries4++; - md5sig->keys4[md5sig->entries4 - 1].addr = addr; - md5sig->keys4[md5sig->entries4 - 1].base.key = newkey; - md5sig->keys4[md5sig->entries4 - 1].base.keylen = newkeylen; + key = sock_kmalloc(sk, sizeof(*key), gfp); + if (!key) + return -ENOMEM; + if (hlist_empty(&md5sig->head) && !tcp_alloc_md5sig_pool(sk)) { + sock_kfree_s(sk, key, sizeof(*key)); + return -ENOMEM; } - return 0; -} -EXPORT_SYMBOL(tcp_v4_md5_do_add); -static int tcp_v4_md5_add_func(struct sock *sk, struct sock *addr_sk, - u8 *newkey, u8 newkeylen) -{ - return tcp_v4_md5_do_add(sk, inet_sk(addr_sk)->inet_daddr, - newkey, newkeylen); + memcpy(key->key, newkey, newkeylen); + key->keylen = newkeylen; + key->family = family; + memcpy(&key->addr, addr, + (family == AF_INET6) ? sizeof(struct in6_addr) : + sizeof(struct in_addr)); + hlist_add_head_rcu(&key->node, &md5sig->head); + return 0; } +EXPORT_SYMBOL(tcp_md5_do_add); -int tcp_v4_md5_do_del(struct sock *sk, __be32 addr) +int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family) { struct tcp_sock *tp = tcp_sk(sk); - int i; - - for (i = 0; i < tp->md5sig_info->entries4; i++) { - if (tp->md5sig_info->keys4[i].addr == addr) { - /* Free the key */ - kfree(tp->md5sig_info->keys4[i].base.key); - tp->md5sig_info->entries4--; - - if (tp->md5sig_info->entries4 == 0) { - kfree(tp->md5sig_info->keys4); - tp->md5sig_info->keys4 = NULL; - tp->md5sig_info->alloced4 = 0; - tcp_free_md5sig_pool(); - } else if (tp->md5sig_info->entries4 != i) { - /* Need to do some manipulation */ - memmove(&tp->md5sig_info->keys4[i], - &tp->md5sig_info->keys4[i+1], - (tp->md5sig_info->entries4 - i) * - sizeof(struct tcp4_md5sig_key)); - } - return 0; - } - } - return -ENOENT; + struct tcp_md5sig_key *key; + struct tcp_md5sig_info *md5sig; + + key = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&addr, AF_INET); + if (!key) + return -ENOENT; + hlist_del_rcu(&key->node); + atomic_sub(sizeof(*key), &sk->sk_omem_alloc); + kfree_rcu(key, rcu); + md5sig = rcu_dereference_protected(tp->md5sig_info, + sock_owned_by_user(sk)); + if (hlist_empty(&md5sig->head)) + tcp_free_md5sig_pool(); + return 0; } -EXPORT_SYMBOL(tcp_v4_md5_do_del); +EXPORT_SYMBOL(tcp_md5_do_del); -static void tcp_v4_clear_md5_list(struct sock *sk) +void tcp_clear_md5_list(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); + struct tcp_md5sig_key *key; + struct hlist_node *pos, *n; + struct tcp_md5sig_info *md5sig; - /* Free each key, then the set of key keys, - * the crypto element, and then decrement our - * hold on the last resort crypto. - */ - if (tp->md5sig_info->entries4) { - int i; - for (i = 0; i < tp->md5sig_info->entries4; i++) - kfree(tp->md5sig_info->keys4[i].base.key); - tp->md5sig_info->entries4 = 0; + md5sig = rcu_dereference_protected(tp->md5sig_info, 1); + + if (!hlist_empty(&md5sig->head)) tcp_free_md5sig_pool(); - } - if (tp->md5sig_info->keys4) { - kfree(tp->md5sig_info->keys4); - tp->md5sig_info->keys4 = NULL; - tp->md5sig_info->alloced4 = 0; + hlist_for_each_entry_safe(key, pos, n, &md5sig->head, node) { + hlist_del_rcu(&key->node); + atomic_sub(sizeof(*key), &sk->sk_omem_alloc); + kfree_rcu(key, rcu); } } @@ -1031,7 +1049,6 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval, { struct tcp_md5sig cmd; struct sockaddr_in *sin = (struct sockaddr_in *)&cmd.tcpm_addr; - u8 *newkey; if (optlen < sizeof(cmd)) return -EINVAL; @@ -1042,32 +1059,16 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval, if (sin->sin_family != AF_INET) return -EINVAL; - if (!cmd.tcpm_key || !cmd.tcpm_keylen) { - if (!tcp_sk(sk)->md5sig_info) - return -ENOENT; - return tcp_v4_md5_do_del(sk, sin->sin_addr.s_addr); - } + if (!cmd.tcpm_key || !cmd.tcpm_keylen) + return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin->sin_addr.s_addr, + AF_INET); if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN) return -EINVAL; - if (!tcp_sk(sk)->md5sig_info) { - struct tcp_sock *tp = tcp_sk(sk); - struct tcp_md5sig_info *p; - - p = kzalloc(sizeof(*p), sk->sk_allocation); - if (!p) - return -EINVAL; - - tp->md5sig_info = p; - sk_nocaps_add(sk, NETIF_F_GSO_MASK); - } - - newkey = kmemdup(cmd.tcpm_key, cmd.tcpm_keylen, sk->sk_allocation); - if (!newkey) - return -ENOMEM; - return tcp_v4_md5_do_add(sk, sin->sin_addr.s_addr, - newkey, cmd.tcpm_keylen); + return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin->sin_addr.s_addr, + AF_INET, cmd.tcpm_key, cmd.tcpm_keylen, + GFP_KERNEL); } static int tcp_v4_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp, @@ -1093,7 +1094,7 @@ static int tcp_v4_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp, return crypto_hash_update(&hp->md5_desc, &sg, sizeof(*bp)); } -static int tcp_v4_md5_hash_hdr(char *md5_hash, struct tcp_md5sig_key *key, +static int tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key, __be32 daddr, __be32 saddr, const struct tcphdr *th) { struct tcp_md5sig_pool *hp; @@ -1193,7 +1194,8 @@ static int tcp_v4_inbound_md5_hash(struct sock *sk, const struct sk_buff *skb) int genhash; unsigned char newhash[16]; - hash_expected = tcp_v4_md5_do_lookup(sk, iph->saddr); + hash_expected = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&iph->saddr, + AF_INET); hash_location = tcp_parse_md5sig_option(th); /* We've parsed the options - do we have a hash? */ @@ -1481,7 +1483,8 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, #ifdef CONFIG_TCP_MD5SIG /* Copy over the MD5 key from the original socket */ - key = tcp_v4_md5_do_lookup(sk, newinet->inet_daddr); + key = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&newinet->inet_daddr, + AF_INET); if (key != NULL) { /* * We're using one, so create a matching key @@ -1489,10 +1492,8 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, * memory, then we end up not copying the key * across. Shucks. */ - char *newkey = kmemdup(key->key, key->keylen, GFP_ATOMIC); - if (newkey != NULL) - tcp_v4_md5_do_add(newsk, newinet->inet_daddr, - newkey, key->keylen); + tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newinet->inet_daddr, + AF_INET, key->key, key->keylen, GFP_ATOMIC); sk_nocaps_add(newsk, NETIF_F_GSO_MASK); } #endif @@ -1853,7 +1854,6 @@ EXPORT_SYMBOL(ipv4_specific); static const struct tcp_sock_af_ops tcp_sock_ipv4_specific = { .md5_lookup = tcp_v4_md5_lookup, .calc_md5_hash = tcp_v4_md5_hash_skb, - .md5_add = tcp_v4_md5_add_func, .md5_parse = tcp_v4_parse_md5_keys, }; #endif @@ -1942,8 +1942,8 @@ void tcp_v4_destroy_sock(struct sock *sk) #ifdef CONFIG_TCP_MD5SIG /* Clean up the MD5 key list, if any */ if (tp->md5sig_info) { - tcp_v4_clear_md5_list(sk); - kfree(tp->md5sig_info); + tcp_clear_md5_list(sk); + kfree_rcu(tp->md5sig_info, rcu); tp->md5sig_info = NULL; } #endif diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 550e755747e0..3cabafb5cdd1 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -359,13 +359,11 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) */ do { struct tcp_md5sig_key *key; - memset(tcptw->tw_md5_key, 0, sizeof(tcptw->tw_md5_key)); - tcptw->tw_md5_keylen = 0; + tcptw->tw_md5_key = NULL; key = tp->af_specific->md5_lookup(sk, sk); if (key != NULL) { - memcpy(&tcptw->tw_md5_key, key->key, key->keylen); - tcptw->tw_md5_keylen = key->keylen; - if (tcp_alloc_md5sig_pool(sk) == NULL) + tcptw->tw_md5_key = kmemdup(key, sizeof(*key), GFP_ATOMIC); + if (tcptw->tw_md5_key && tcp_alloc_md5sig_pool(sk) == NULL) BUG(); } } while (0); @@ -405,8 +403,10 @@ void tcp_twsk_destructor(struct sock *sk) { #ifdef CONFIG_TCP_MD5SIG struct tcp_timewait_sock *twsk = tcp_twsk(sk); - if (twsk->tw_md5_keylen) + if (twsk->tw_md5_key) { tcp_free_md5sig_pool(); + kfree_rcu(twsk->tw_md5_key, rcu); + } #endif } EXPORT_SYMBOL_GPL(tcp_twsk_destructor); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 4ff3b6dc74fc..364784a91939 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2306,8 +2306,10 @@ begin_fwd: if (sacked & (TCPCB_SACKED_ACKED|TCPCB_SACKED_RETRANS)) continue; - if (tcp_retransmit_skb(sk, skb)) + if (tcp_retransmit_skb(sk, skb)) { + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPRETRANSFAIL); return; + } NET_INC_STATS_BH(sock_net(sk), mib_idx); if (inet_csk(sk)->icsk_ca_state == TCP_CA_Recovery) diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c index 59402b4637f9..db00d27ffb16 100644 --- a/net/ipv6/anycast.c +++ b/net/ipv6/anycast.c @@ -211,35 +211,6 @@ void ipv6_sock_ac_close(struct sock *sk) rcu_read_unlock(); } -#if 0 -/* The function is not used, which is funny. Apparently, author - * supposed to use it to filter out datagrams inside udp/raw but forgot. - * - * It is OK, anycasts are not special comparing to delivery to unicasts. - */ - -int inet6_ac_check(struct sock *sk, struct in6_addr *addr, int ifindex) -{ - struct ipv6_ac_socklist *pac; - struct ipv6_pinfo *np = inet6_sk(sk); - int found; - - found = 0; - read_lock(&ipv6_sk_ac_lock); - for (pac=np->ipv6_ac_list; pac; pac=pac->acl_next) { - if (ifindex && pac->acl_ifindex != ifindex) - continue; - found = ipv6_addr_equal(&pac->acl_addr, addr); - if (found) - break; - } - read_unlock(&ipv6_sk_ac_lock); - - return found; -} - -#endif - static void aca_put(struct ifacaddr6 *ac) { if (atomic_dec_and_test(&ac->aca_refcnt)) { diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index b82bcde53f7a..5b27fbcae346 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -1552,11 +1552,20 @@ static int fib6_age(struct rt6_info *rt, void *arg) time_after_eq(now, rt->dst.lastuse + gc_args.timeout)) { RT6_TRACE("aging clone %p\n", rt); return -1; - } else if ((rt->rt6i_flags & RTF_GATEWAY) && - (!(dst_get_neighbour_noref_raw(&rt->dst)->flags & NTF_ROUTER))) { - RT6_TRACE("purging route %p via non-router but gateway\n", - rt); - return -1; + } else if (rt->rt6i_flags & RTF_GATEWAY) { + struct neighbour *neigh; + __u8 neigh_flags = 0; + + neigh = dst_neigh_lookup(&rt->dst, &rt->rt6i_gateway); + if (neigh) { + neigh_flags = neigh->flags; + neigh_release(neigh); + } + if (neigh_flags & NTF_ROUTER) { + RT6_TRACE("purging route %p via non-router but gateway\n", + rt); + return -1; + } } gc_args.more++; } diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index d97e07183ce9..7a98fc2a5d97 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -388,7 +388,6 @@ int ip6_forward(struct sk_buff *skb) struct ipv6hdr *hdr = ipv6_hdr(skb); struct inet6_skb_parm *opt = IP6CB(skb); struct net *net = dev_net(dst->dev); - struct neighbour *n; u32 mtu; if (net->ipv6.devconf_all->forwarding == 0) @@ -463,8 +462,7 @@ int ip6_forward(struct sk_buff *skb) send redirects to source routed frames. We don't send redirects to frames decapsulated from IPsec. */ - n = dst_get_neighbour_noref(dst); - if (skb->dev == dst->dev && n && opt->srcrt == 0 && !skb_sec_path(skb)) { + if (skb->dev == dst->dev && opt->srcrt == 0 && !skb_sec_path(skb)) { struct in6_addr *target = NULL; struct rt6_info *rt; @@ -474,8 +472,8 @@ int ip6_forward(struct sk_buff *skb) */ rt = (struct rt6_info *) dst; - if ((rt->rt6i_flags & RTF_GATEWAY)) - target = (struct in6_addr*)&n->primary_key; + if (rt->rt6i_flags & RTF_GATEWAY) + target = &rt->rt6i_gateway; else target = &hdr->daddr; @@ -486,7 +484,7 @@ int ip6_forward(struct sk_buff *skb) and by source (inside ndisc_send_redirect) */ if (inet_peer_xrlim_allow(rt->rt6i_peer, 1*HZ)) - ndisc_send_redirect(skb, n, target); + ndisc_send_redirect(skb, target); } else { int addrtype = ipv6_addr_type(&hdr->saddr); diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index d8f02ef88e59..8d817018c188 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1223,11 +1223,17 @@ static void ndisc_router_discovery(struct sk_buff *skb) rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev); - if (rt) - neigh = dst_get_neighbour_noref(&rt->dst); - + if (rt) { + neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr); + if (!neigh) { + ND_PRINTK0(KERN_ERR + "ICMPv6 RA: %s() got default router without neighbour.\n", + __func__); + dst_release(&rt->dst); + return; + } + } if (rt && lifetime == 0) { - neigh_clone(neigh); ip6_del_rt(rt); rt = NULL; } @@ -1244,7 +1250,7 @@ static void ndisc_router_discovery(struct sk_buff *skb) return; } - neigh = dst_get_neighbour_noref(&rt->dst); + neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr); if (neigh == NULL) { ND_PRINTK0(KERN_ERR "ICMPv6 RA: %s() got default router without neighbour.\n", @@ -1411,7 +1417,7 @@ skip_routeinfo: out: if (rt) dst_release(&rt->dst); - else if (neigh) + if (neigh) neigh_release(neigh); } @@ -1506,8 +1512,7 @@ static void ndisc_redirect_rcv(struct sk_buff *skb) } } -void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh, - const struct in6_addr *target) +void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) { struct net_device *dev = skb->dev; struct net *net = dev_net(dev); @@ -1565,6 +1570,13 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh, goto release; if (dev->addr_len) { + struct neighbour *neigh = dst_neigh_lookup(skb_dst(skb), target); + if (!neigh) { + ND_PRINTK2(KERN_WARNING + "ICMPv6 Redirect: no neigh for target address\n"); + goto release; + } + read_lock_bh(&neigh->lock); if (neigh->nud_state & NUD_VALID) { memcpy(ha_buf, neigh->ha, dev->addr_len); @@ -1573,6 +1585,8 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh, len += ndisc_opt_addr_space(dev); } else read_unlock_bh(&neigh->lock); + + neigh_release(neigh); } rd_len = min_t(unsigned int, diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index b69fae76a6f1..9447bd69873a 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -336,12 +336,11 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, } found: - /* RFC5722, Section 4: - * When reassembling an IPv6 datagram, if + /* RFC5722, Section 4, amended by Errata ID : 3089 + * When reassembling an IPv6 datagram, if * one or more its constituent fragments is determined to be an * overlapping fragment, the entire datagram (and any constituent - * fragments, including those not yet received) MUST be silently - * discarded. + * fragments) MUST be silently discarded. */ /* Check for overlap with preceding fragment. */ diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 8c2e3ab58f2a..92be12bb8d23 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -121,9 +121,22 @@ static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old) return p; } +static inline const void *choose_neigh_daddr(struct rt6_info *rt, const void *daddr) +{ + struct in6_addr *p = &rt->rt6i_gateway; + + if (!ipv6_addr_any(p)) + return (const void *) p; + return daddr; +} + static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst, const void *daddr) { - struct neighbour *n = __ipv6_neigh_lookup(&nd_tbl, dst->dev, daddr); + struct rt6_info *rt = (struct rt6_info *) dst; + struct neighbour *n; + + daddr = choose_neigh_daddr(rt, daddr); + n = __ipv6_neigh_lookup(&nd_tbl, dst->dev, daddr); if (n) return n; return neigh_create(&nd_tbl, daddr, dst->dev); diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 133768e52912..c4ffd1743528 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -680,9 +680,10 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, /* ISATAP (RFC4214) - must come before 6to4 */ if (dev->priv_flags & IFF_ISATAP) { struct neighbour *neigh = NULL; + bool do_tx_error = false; if (skb_dst(skb)) - neigh = dst_get_neighbour_noref(skb_dst(skb)); + neigh = dst_neigh_lookup(skb_dst(skb), &iph6->daddr); if (neigh == NULL) { if (net_ratelimit()) @@ -697,6 +698,10 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, ipv6_addr_is_isatap(addr6)) dst = addr6->s6_addr32[3]; else + do_tx_error = true; + + neigh_release(neigh); + if (do_tx_error) goto tx_error; } @@ -705,9 +710,10 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, if (!dst) { struct neighbour *neigh = NULL; + bool do_tx_error = false; if (skb_dst(skb)) - neigh = dst_get_neighbour_noref(skb_dst(skb)); + neigh = dst_neigh_lookup(skb_dst(skb), &iph6->daddr); if (neigh == NULL) { if (net_ratelimit()) @@ -723,10 +729,14 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, addr_type = ipv6_addr_type(addr6); } - if ((addr_type & IPV6_ADDR_COMPATv4) == 0) - goto tx_error_icmp; + if ((addr_type & IPV6_ADDR_COMPATv4) != 0) + dst = addr6->s6_addr32[3]; + else + do_tx_error = true; - dst = addr6->s6_addr32[3]; + neigh_release(neigh); + if (do_tx_error) + goto tx_error; } rt = ip_route_output_ports(dev_net(dev), &fl4, NULL, diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 3edd05ae4388..d16414cb3421 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -540,19 +540,7 @@ static void tcp_v6_reqsk_destructor(struct request_sock *req) static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(struct sock *sk, const struct in6_addr *addr) { - struct tcp_sock *tp = tcp_sk(sk); - int i; - - BUG_ON(tp == NULL); - - if (!tp->md5sig_info || !tp->md5sig_info->entries6) - return NULL; - - for (i = 0; i < tp->md5sig_info->entries6; i++) { - if (ipv6_addr_equal(&tp->md5sig_info->keys6[i].addr, addr)) - return &tp->md5sig_info->keys6[i].base; - } - return NULL; + return tcp_md5_do_lookup(sk, (union tcp_md5_addr *)addr, AF_INET6); } static struct tcp_md5sig_key *tcp_v6_md5_lookup(struct sock *sk, @@ -567,136 +555,11 @@ 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, const struct in6_addr *peer, - char *newkey, u8 newkeylen) -{ - /* Add key to the list */ - struct tcp_md5sig_key *key; - struct tcp_sock *tp = tcp_sk(sk); - struct tcp6_md5sig_key *keys; - - key = tcp_v6_md5_do_lookup(sk, peer); - if (key) { - /* modify existing entry - just update that one */ - kfree(key->key); - key->key = newkey; - key->keylen = newkeylen; - } else { - /* reallocate new list if current one is full. */ - if (!tp->md5sig_info) { - tp->md5sig_info = kzalloc(sizeof(*tp->md5sig_info), GFP_ATOMIC); - if (!tp->md5sig_info) { - kfree(newkey); - return -ENOMEM; - } - sk_nocaps_add(sk, NETIF_F_GSO_MASK); - } - if (tp->md5sig_info->entries6 == 0 && - tcp_alloc_md5sig_pool(sk) == NULL) { - kfree(newkey); - return -ENOMEM; - } - if (tp->md5sig_info->alloced6 == tp->md5sig_info->entries6) { - keys = kmalloc((sizeof (tp->md5sig_info->keys6[0]) * - (tp->md5sig_info->entries6 + 1)), GFP_ATOMIC); - - if (!keys) { - kfree(newkey); - if (tp->md5sig_info->entries6 == 0) - tcp_free_md5sig_pool(); - return -ENOMEM; - } - - if (tp->md5sig_info->entries6) - memmove(keys, tp->md5sig_info->keys6, - (sizeof (tp->md5sig_info->keys6[0]) * - tp->md5sig_info->entries6)); - - kfree(tp->md5sig_info->keys6); - tp->md5sig_info->keys6 = keys; - tp->md5sig_info->alloced6++; - } - - tp->md5sig_info->keys6[tp->md5sig_info->entries6].addr = *peer; - tp->md5sig_info->keys6[tp->md5sig_info->entries6].base.key = newkey; - tp->md5sig_info->keys6[tp->md5sig_info->entries6].base.keylen = newkeylen; - - tp->md5sig_info->entries6++; - } - return 0; -} - -static int tcp_v6_md5_add_func(struct sock *sk, struct sock *addr_sk, - u8 *newkey, __u8 newkeylen) -{ - return tcp_v6_md5_do_add(sk, &inet6_sk(addr_sk)->daddr, - newkey, newkeylen); -} - -static int tcp_v6_md5_do_del(struct sock *sk, const struct in6_addr *peer) -{ - struct tcp_sock *tp = tcp_sk(sk); - int i; - - for (i = 0; i < tp->md5sig_info->entries6; i++) { - if (ipv6_addr_equal(&tp->md5sig_info->keys6[i].addr, peer)) { - /* Free the key */ - kfree(tp->md5sig_info->keys6[i].base.key); - tp->md5sig_info->entries6--; - - if (tp->md5sig_info->entries6 == 0) { - kfree(tp->md5sig_info->keys6); - tp->md5sig_info->keys6 = NULL; - tp->md5sig_info->alloced6 = 0; - tcp_free_md5sig_pool(); - } else { - /* shrink the database */ - if (tp->md5sig_info->entries6 != i) - memmove(&tp->md5sig_info->keys6[i], - &tp->md5sig_info->keys6[i+1], - (tp->md5sig_info->entries6 - i) - * sizeof (tp->md5sig_info->keys6[0])); - } - return 0; - } - } - return -ENOENT; -} - -static void tcp_v6_clear_md5_list (struct sock *sk) -{ - struct tcp_sock *tp = tcp_sk(sk); - int i; - - if (tp->md5sig_info->entries6) { - for (i = 0; i < tp->md5sig_info->entries6; i++) - kfree(tp->md5sig_info->keys6[i].base.key); - tp->md5sig_info->entries6 = 0; - tcp_free_md5sig_pool(); - } - - kfree(tp->md5sig_info->keys6); - tp->md5sig_info->keys6 = NULL; - tp->md5sig_info->alloced6 = 0; - - if (tp->md5sig_info->entries4) { - for (i = 0; i < tp->md5sig_info->entries4; i++) - kfree(tp->md5sig_info->keys4[i].base.key); - tp->md5sig_info->entries4 = 0; - tcp_free_md5sig_pool(); - } - - kfree(tp->md5sig_info->keys4); - tp->md5sig_info->keys4 = NULL; - tp->md5sig_info->alloced4 = 0; -} - static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval, int optlen) { struct tcp_md5sig cmd; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.tcpm_addr; - u8 *newkey; if (optlen < sizeof(cmd)) return -EINVAL; @@ -708,36 +571,22 @@ static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval, return -EINVAL; if (!cmd.tcpm_keylen) { - if (!tcp_sk(sk)->md5sig_info) - return -ENOENT; if (ipv6_addr_v4mapped(&sin6->sin6_addr)) - return tcp_v4_md5_do_del(sk, sin6->sin6_addr.s6_addr32[3]); - return tcp_v6_md5_do_del(sk, &sin6->sin6_addr); + return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3], + AF_INET); + return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin6->sin6_addr, + AF_INET6); } if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN) return -EINVAL; - if (!tcp_sk(sk)->md5sig_info) { - struct tcp_sock *tp = tcp_sk(sk); - struct tcp_md5sig_info *p; - - p = kzalloc(sizeof(struct tcp_md5sig_info), GFP_KERNEL); - if (!p) - return -ENOMEM; + if (ipv6_addr_v4mapped(&sin6->sin6_addr)) + return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3], + AF_INET, cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL); - tp->md5sig_info = p; - sk_nocaps_add(sk, NETIF_F_GSO_MASK); - } - - newkey = kmemdup(cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL); - if (!newkey) - return -ENOMEM; - if (ipv6_addr_v4mapped(&sin6->sin6_addr)) { - return tcp_v4_md5_do_add(sk, sin6->sin6_addr.s6_addr32[3], - newkey, cmd.tcpm_keylen); - } - return tcp_v6_md5_do_add(sk, &sin6->sin6_addr, newkey, cmd.tcpm_keylen); + return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr, + AF_INET6, cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL); } static int tcp_v6_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp, @@ -1074,6 +923,13 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb) const struct tcphdr *th = tcp_hdr(skb); u32 seq = 0, ack_seq = 0; struct tcp_md5sig_key *key = NULL; +#ifdef CONFIG_TCP_MD5SIG + const __u8 *hash_location = NULL; + struct ipv6hdr *ipv6h = ipv6_hdr(skb); + unsigned char newhash[16]; + int genhash; + struct sock *sk1 = NULL; +#endif if (th->rst) return; @@ -1082,8 +938,32 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb) return; #ifdef CONFIG_TCP_MD5SIG - if (sk) - key = tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->saddr); + hash_location = tcp_parse_md5sig_option(th); + if (!sk && hash_location) { + /* + * active side is lost. Try to find listening socket through + * source port, and then find md5 key through listening socket. + * we are not loose security here: + * Incoming packet is checked with md5 hash with finding key, + * no RST generated if md5 hash doesn't match. + */ + sk1 = inet6_lookup_listener(dev_net(skb_dst(skb)->dev), + &tcp_hashinfo, &ipv6h->daddr, + ntohs(th->source), inet6_iif(skb)); + if (!sk1) + return; + + rcu_read_lock(); + key = tcp_v6_md5_do_lookup(sk1, &ipv6h->saddr); + if (!key) + goto release_sk1; + + genhash = tcp_v6_md5_hash_skb(newhash, key, NULL, NULL, skb); + if (genhash || memcmp(hash_location, newhash, 16) != 0) + goto release_sk1; + } else { + key = sk ? tcp_v6_md5_do_lookup(sk, &ipv6h->saddr) : NULL; + } #endif if (th->ack) @@ -1093,6 +973,14 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb) (th->doff << 2); tcp_v6_send_response(skb, seq, ack_seq, 0, 0, key, 1, 0); + +#ifdef CONFIG_TCP_MD5SIG +release_sk1: + if (sk1) { + rcu_read_unlock(); + sock_put(sk1); + } +#endif } static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 ts, @@ -1510,10 +1398,8 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, * memory, then we end up not copying the key * across. Shucks. */ - char *newkey = kmemdup(key->key, key->keylen, GFP_ATOMIC); - if (newkey != NULL) - tcp_v6_md5_do_add(newsk, &newnp->daddr, - newkey, key->keylen); + tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newnp->daddr, + AF_INET6, key->key, key->keylen, GFP_ATOMIC); } #endif @@ -1898,7 +1784,6 @@ static const struct inet_connection_sock_af_ops ipv6_specific = { static const struct tcp_sock_af_ops tcp_sock_ipv6_specific = { .md5_lookup = tcp_v6_md5_lookup, .calc_md5_hash = tcp_v6_md5_hash_skb, - .md5_add = tcp_v6_md5_add_func, .md5_parse = tcp_v6_parse_md5_keys, }; #endif @@ -1930,7 +1815,6 @@ static const struct inet_connection_sock_af_ops ipv6_mapped = { static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific = { .md5_lookup = tcp_v4_md5_lookup, .calc_md5_hash = tcp_v4_md5_hash_skb, - .md5_add = tcp_v6_md5_add_func, .md5_parse = tcp_v6_parse_md5_keys, }; #endif @@ -2004,11 +1888,6 @@ static int tcp_v6_init_sock(struct sock *sk) static void tcp_v6_destroy_sock(struct sock *sk) { -#ifdef CONFIG_TCP_MD5SIG - /* Clean up the MD5 key list */ - if (tcp_sk(sk)->md5sig_info) - tcp_v6_clear_md5_list(sk); -#endif tcp_v4_destroy_sock(sk); inet6_destroy_sock(sk); } diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c index 4eeff89c1aaa..8755a3079d0f 100644 --- a/net/ipv6/xfrm6_output.c +++ b/net/ipv6/xfrm6_output.c @@ -146,7 +146,7 @@ static int __xfrm6_output(struct sk_buff *skb) return -EMSGSIZE; } - if ((x && x->props.mode == XFRM_MODE_TUNNEL) && + if (x->props.mode == XFRM_MODE_TUNNEL && ((skb->len > mtu && !skb_is_gso(skb)) || dst_allfrag(skb_dst(skb)))) { return ip6_fragment(skb, x->outer_mode->afinfo->output_finish); diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 629b06182f3f..4d751e3d4b4b 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1645,6 +1645,24 @@ static void netlink_destroy_callback(struct netlink_callback *cb) kfree(cb); } +struct nlmsghdr * +__nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, int type, int len, int flags) +{ + struct nlmsghdr *nlh; + int size = NLMSG_LENGTH(len); + + nlh = (struct nlmsghdr*)skb_put(skb, NLMSG_ALIGN(size)); + nlh->nlmsg_type = type; + nlh->nlmsg_len = size; + nlh->nlmsg_flags = flags; + nlh->nlmsg_pid = pid; + nlh->nlmsg_seq = seq; + if (!__builtin_constant_p(size) || NLMSG_ALIGN(size) - size != 0) + memset(NLMSG_DATA(nlh) + len, 0, NLMSG_ALIGN(size) - size); + return nlh; +} +EXPORT_SYMBOL(__nlmsg_put); + /* * It looks a bit ugly. * It would be better to create kernel thread. diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index c29d2568c9e0..a1154717219e 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -498,6 +498,37 @@ int genl_unregister_family(struct genl_family *family) } EXPORT_SYMBOL(genl_unregister_family); +/** + * genlmsg_put - Add generic netlink header to netlink message + * @skb: socket buffer holding the message + * @pid: netlink pid the message is addressed to + * @seq: sequence number (usually the one of the sender) + * @family: generic netlink family + * @flags netlink message flags + * @cmd: generic netlink command + * + * Returns pointer to user specific header + */ +void *genlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, + struct genl_family *family, int flags, u8 cmd) +{ + struct nlmsghdr *nlh; + struct genlmsghdr *hdr; + + nlh = nlmsg_put(skb, pid, seq, family->id, GENL_HDRLEN + + family->hdrsize, flags); + if (nlh == NULL) + return NULL; + + hdr = nlmsg_data(nlh); + hdr->cmd = cmd; + hdr->version = family->version; + hdr->reserved = 0; + + return (char *) hdr + GENL_HDRLEN; +} +EXPORT_SYMBOL(genlmsg_put); + static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) { struct genl_ops *ops; |