| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * INET An implementation of the TCP/IP protocol suite for the LINUX |
|---|
| 3 | 4 | * operating system. INET is implemented using the BSD Socket |
|---|
| .. | .. |
|---|
| 22 | 23 | * interface. |
|---|
| 23 | 24 | * Alexey Kuznetsov: Potential hang under some extreme |
|---|
| 24 | 25 | * cases removed. |
|---|
| 25 | | - * |
|---|
| 26 | | - * This program is free software; you can redistribute it and/or |
|---|
| 27 | | - * modify it under the terms of the GNU General Public License |
|---|
| 28 | | - * as published by the Free Software Foundation; either version |
|---|
| 29 | | - * 2 of the License, or (at your option) any later version. |
|---|
| 30 | 26 | */ |
|---|
| 31 | 27 | #include <linux/kernel.h> |
|---|
| 32 | 28 | #include <linux/jiffies.h> |
|---|
| .. | .. |
|---|
| 59 | 55 | #include <net/net_namespace.h> |
|---|
| 60 | 56 | #include <linux/u64_stats_sync.h> |
|---|
| 61 | 57 | |
|---|
| 62 | | -struct pcpu_lstats { |
|---|
| 63 | | - u64 packets; |
|---|
| 64 | | - u64 bytes; |
|---|
| 65 | | - struct u64_stats_sync syncp; |
|---|
| 66 | | -}; |
|---|
| 58 | +/* blackhole_netdev - a device used for dsts that are marked expired! |
|---|
| 59 | + * This is global device (instead of per-net-ns) since it's not needed |
|---|
| 60 | + * to be per-ns and gets initialized at boot time. |
|---|
| 61 | + */ |
|---|
| 62 | +struct net_device *blackhole_netdev; |
|---|
| 63 | +EXPORT_SYMBOL(blackhole_netdev); |
|---|
| 67 | 64 | |
|---|
| 68 | 65 | /* The higher levels take care of making this non-reentrant (it's |
|---|
| 69 | 66 | * called with bh's disabled). |
|---|
| .. | .. |
|---|
| 71 | 68 | static netdev_tx_t loopback_xmit(struct sk_buff *skb, |
|---|
| 72 | 69 | struct net_device *dev) |
|---|
| 73 | 70 | { |
|---|
| 74 | | - struct pcpu_lstats *lb_stats; |
|---|
| 75 | 71 | int len; |
|---|
| 76 | 72 | |
|---|
| 77 | 73 | skb_tx_timestamp(skb); |
|---|
| .. | .. |
|---|
| 88 | 84 | |
|---|
| 89 | 85 | skb->protocol = eth_type_trans(skb, dev); |
|---|
| 90 | 86 | |
|---|
| 91 | | - /* it's OK to use per_cpu_ptr() because BHs are off */ |
|---|
| 92 | | - lb_stats = this_cpu_ptr(dev->lstats); |
|---|
| 93 | | - |
|---|
| 94 | 87 | len = skb->len; |
|---|
| 95 | | - if (likely(netif_rx(skb) == NET_RX_SUCCESS)) { |
|---|
| 96 | | - u64_stats_update_begin(&lb_stats->syncp); |
|---|
| 97 | | - lb_stats->bytes += len; |
|---|
| 98 | | - lb_stats->packets++; |
|---|
| 99 | | - u64_stats_update_end(&lb_stats->syncp); |
|---|
| 100 | | - } |
|---|
| 88 | + if (likely(netif_rx(skb) == NET_RX_SUCCESS)) |
|---|
| 89 | + dev_lstats_add(dev, len); |
|---|
| 101 | 90 | |
|---|
| 102 | 91 | return NETDEV_TX_OK; |
|---|
| 103 | 92 | } |
|---|
| 104 | 93 | |
|---|
| 105 | | -static void loopback_get_stats64(struct net_device *dev, |
|---|
| 106 | | - struct rtnl_link_stats64 *stats) |
|---|
| 94 | +void dev_lstats_read(struct net_device *dev, u64 *packets, u64 *bytes) |
|---|
| 107 | 95 | { |
|---|
| 108 | | - u64 bytes = 0; |
|---|
| 109 | | - u64 packets = 0; |
|---|
| 110 | 96 | int i; |
|---|
| 97 | + |
|---|
| 98 | + *packets = 0; |
|---|
| 99 | + *bytes = 0; |
|---|
| 111 | 100 | |
|---|
| 112 | 101 | for_each_possible_cpu(i) { |
|---|
| 113 | 102 | const struct pcpu_lstats *lb_stats; |
|---|
| .. | .. |
|---|
| 117 | 106 | lb_stats = per_cpu_ptr(dev->lstats, i); |
|---|
| 118 | 107 | do { |
|---|
| 119 | 108 | start = u64_stats_fetch_begin_irq(&lb_stats->syncp); |
|---|
| 120 | | - tbytes = lb_stats->bytes; |
|---|
| 121 | | - tpackets = lb_stats->packets; |
|---|
| 109 | + tpackets = u64_stats_read(&lb_stats->packets); |
|---|
| 110 | + tbytes = u64_stats_read(&lb_stats->bytes); |
|---|
| 122 | 111 | } while (u64_stats_fetch_retry_irq(&lb_stats->syncp, start)); |
|---|
| 123 | | - bytes += tbytes; |
|---|
| 124 | | - packets += tpackets; |
|---|
| 112 | + *bytes += tbytes; |
|---|
| 113 | + *packets += tpackets; |
|---|
| 125 | 114 | } |
|---|
| 115 | +} |
|---|
| 116 | +EXPORT_SYMBOL(dev_lstats_read); |
|---|
| 117 | + |
|---|
| 118 | +static void loopback_get_stats64(struct net_device *dev, |
|---|
| 119 | + struct rtnl_link_stats64 *stats) |
|---|
| 120 | +{ |
|---|
| 121 | + u64 packets, bytes; |
|---|
| 122 | + |
|---|
| 123 | + dev_lstats_read(dev, &packets, &bytes); |
|---|
| 124 | + |
|---|
| 126 | 125 | stats->rx_packets = packets; |
|---|
| 127 | 126 | stats->tx_packets = packets; |
|---|
| 128 | 127 | stats->rx_bytes = bytes; |
|---|
| .. | .. |
|---|
| 134 | 133 | return 1; |
|---|
| 135 | 134 | } |
|---|
| 136 | 135 | |
|---|
| 137 | | -static int loopback_get_ts_info(struct net_device *netdev, |
|---|
| 138 | | - struct ethtool_ts_info *ts_info) |
|---|
| 139 | | -{ |
|---|
| 140 | | - ts_info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | |
|---|
| 141 | | - SOF_TIMESTAMPING_RX_SOFTWARE | |
|---|
| 142 | | - SOF_TIMESTAMPING_SOFTWARE; |
|---|
| 143 | | - |
|---|
| 144 | | - ts_info->phc_index = -1; |
|---|
| 145 | | - |
|---|
| 146 | | - return 0; |
|---|
| 147 | | -}; |
|---|
| 148 | | - |
|---|
| 149 | 136 | static const struct ethtool_ops loopback_ethtool_ops = { |
|---|
| 150 | 137 | .get_link = always_on, |
|---|
| 151 | | - .get_ts_info = loopback_get_ts_info, |
|---|
| 138 | + .get_ts_info = ethtool_op_get_ts_info, |
|---|
| 152 | 139 | }; |
|---|
| 153 | 140 | |
|---|
| 154 | 141 | static int loopback_dev_init(struct net_device *dev) |
|---|
| .. | .. |
|---|
| 172 | 159 | .ndo_set_mac_address = eth_mac_addr, |
|---|
| 173 | 160 | }; |
|---|
| 174 | 161 | |
|---|
| 175 | | -/* The loopback device is special. There is only one instance |
|---|
| 176 | | - * per network namespace. |
|---|
| 177 | | - */ |
|---|
| 178 | | -static void loopback_setup(struct net_device *dev) |
|---|
| 162 | +static void gen_lo_setup(struct net_device *dev, |
|---|
| 163 | + unsigned int mtu, |
|---|
| 164 | + const struct ethtool_ops *eth_ops, |
|---|
| 165 | + const struct header_ops *hdr_ops, |
|---|
| 166 | + const struct net_device_ops *dev_ops, |
|---|
| 167 | + void (*dev_destructor)(struct net_device *dev)) |
|---|
| 179 | 168 | { |
|---|
| 180 | | - dev->mtu = 64 * 1024; |
|---|
| 169 | + dev->mtu = mtu; |
|---|
| 181 | 170 | dev->hard_header_len = ETH_HLEN; /* 14 */ |
|---|
| 182 | 171 | dev->min_header_len = ETH_HLEN; /* 14 */ |
|---|
| 183 | 172 | dev->addr_len = ETH_ALEN; /* 6 */ |
|---|
| .. | .. |
|---|
| 196 | 185 | | NETIF_F_NETNS_LOCAL |
|---|
| 197 | 186 | | NETIF_F_VLAN_CHALLENGED |
|---|
| 198 | 187 | | NETIF_F_LOOPBACK; |
|---|
| 199 | | - dev->ethtool_ops = &loopback_ethtool_ops; |
|---|
| 200 | | - dev->header_ops = ð_header_ops; |
|---|
| 201 | | - dev->netdev_ops = &loopback_ops; |
|---|
| 188 | + dev->ethtool_ops = eth_ops; |
|---|
| 189 | + dev->header_ops = hdr_ops; |
|---|
| 190 | + dev->netdev_ops = dev_ops; |
|---|
| 202 | 191 | dev->needs_free_netdev = true; |
|---|
| 203 | | - dev->priv_destructor = loopback_dev_free; |
|---|
| 192 | + dev->priv_destructor = dev_destructor; |
|---|
| 193 | +} |
|---|
| 194 | + |
|---|
| 195 | +/* The loopback device is special. There is only one instance |
|---|
| 196 | + * per network namespace. |
|---|
| 197 | + */ |
|---|
| 198 | +static void loopback_setup(struct net_device *dev) |
|---|
| 199 | +{ |
|---|
| 200 | + gen_lo_setup(dev, (64 * 1024), &loopback_ethtool_ops, ð_header_ops, |
|---|
| 201 | + &loopback_ops, loopback_dev_free); |
|---|
| 204 | 202 | } |
|---|
| 205 | 203 | |
|---|
| 206 | 204 | /* Setup and register the loopback device. */ |
|---|
| .. | .. |
|---|
| 210 | 208 | int err; |
|---|
| 211 | 209 | |
|---|
| 212 | 210 | err = -ENOMEM; |
|---|
| 213 | | - dev = alloc_netdev(0, "lo", NET_NAME_UNKNOWN, loopback_setup); |
|---|
| 211 | + dev = alloc_netdev(0, "lo", NET_NAME_PREDICTABLE, loopback_setup); |
|---|
| 214 | 212 | if (!dev) |
|---|
| 215 | 213 | goto out; |
|---|
| 216 | 214 | |
|---|
| .. | .. |
|---|
| 235 | 233 | struct pernet_operations __net_initdata loopback_net_ops = { |
|---|
| 236 | 234 | .init = loopback_net_init, |
|---|
| 237 | 235 | }; |
|---|
| 236 | + |
|---|
| 237 | +/* blackhole netdevice */ |
|---|
| 238 | +static netdev_tx_t blackhole_netdev_xmit(struct sk_buff *skb, |
|---|
| 239 | + struct net_device *dev) |
|---|
| 240 | +{ |
|---|
| 241 | + kfree_skb(skb); |
|---|
| 242 | + net_warn_ratelimited("%s(): Dropping skb.\n", __func__); |
|---|
| 243 | + return NETDEV_TX_OK; |
|---|
| 244 | +} |
|---|
| 245 | + |
|---|
| 246 | +static const struct net_device_ops blackhole_netdev_ops = { |
|---|
| 247 | + .ndo_start_xmit = blackhole_netdev_xmit, |
|---|
| 248 | +}; |
|---|
| 249 | + |
|---|
| 250 | +/* This is a dst-dummy device used specifically for invalidated |
|---|
| 251 | + * DSTs and unlike loopback, this is not per-ns. |
|---|
| 252 | + */ |
|---|
| 253 | +static void blackhole_netdev_setup(struct net_device *dev) |
|---|
| 254 | +{ |
|---|
| 255 | + gen_lo_setup(dev, ETH_MIN_MTU, NULL, NULL, &blackhole_netdev_ops, NULL); |
|---|
| 256 | +} |
|---|
| 257 | + |
|---|
| 258 | +/* Setup and register the blackhole_netdev. */ |
|---|
| 259 | +static int __init blackhole_netdev_init(void) |
|---|
| 260 | +{ |
|---|
| 261 | + blackhole_netdev = alloc_netdev(0, "blackhole_dev", NET_NAME_UNKNOWN, |
|---|
| 262 | + blackhole_netdev_setup); |
|---|
| 263 | + if (!blackhole_netdev) |
|---|
| 264 | + return -ENOMEM; |
|---|
| 265 | + |
|---|
| 266 | + rtnl_lock(); |
|---|
| 267 | + dev_init_scheduler(blackhole_netdev); |
|---|
| 268 | + dev_activate(blackhole_netdev); |
|---|
| 269 | + rtnl_unlock(); |
|---|
| 270 | + |
|---|
| 271 | + blackhole_netdev->flags |= IFF_UP | IFF_RUNNING; |
|---|
| 272 | + dev_net_set(blackhole_netdev, &init_net); |
|---|
| 273 | + |
|---|
| 274 | + return 0; |
|---|
| 275 | +} |
|---|
| 276 | + |
|---|
| 277 | +device_initcall(blackhole_netdev_init); |
|---|