1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | #include <linux/ethtool.h> |
3 | #include <linux/module.h> |
4 | #include <linux/kernel.h> |
5 | #include <linux/netdevice.h> |
6 | #include <linux/netlink.h> |
7 | #include <net/net_namespace.h> |
8 | #include <linux/if_arp.h> |
9 | #include <net/rtnetlink.h> |
10 | |
11 | static netdev_tx_t nlmon_xmit(struct sk_buff *skb, struct net_device *dev) |
12 | { |
13 | dev_lstats_add(dev, len: skb->len); |
14 | |
15 | dev_kfree_skb(skb); |
16 | |
17 | return NETDEV_TX_OK; |
18 | } |
19 | |
20 | struct nlmon { |
21 | struct netlink_tap nt; |
22 | }; |
23 | |
24 | static int nlmon_open(struct net_device *dev) |
25 | { |
26 | struct nlmon *nlmon = netdev_priv(dev); |
27 | |
28 | nlmon->nt.dev = dev; |
29 | nlmon->nt.module = THIS_MODULE; |
30 | return netlink_add_tap(nt: &nlmon->nt); |
31 | } |
32 | |
33 | static int nlmon_close(struct net_device *dev) |
34 | { |
35 | struct nlmon *nlmon = netdev_priv(dev); |
36 | |
37 | return netlink_remove_tap(nt: &nlmon->nt); |
38 | } |
39 | |
40 | static void |
41 | nlmon_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) |
42 | { |
43 | dev_lstats_read(dev, packets: &stats->rx_packets, bytes: &stats->rx_bytes); |
44 | } |
45 | |
46 | static u32 always_on(struct net_device *dev) |
47 | { |
48 | return 1; |
49 | } |
50 | |
51 | static const struct ethtool_ops nlmon_ethtool_ops = { |
52 | .get_link = always_on, |
53 | }; |
54 | |
55 | static const struct net_device_ops nlmon_ops = { |
56 | .ndo_open = nlmon_open, |
57 | .ndo_stop = nlmon_close, |
58 | .ndo_start_xmit = nlmon_xmit, |
59 | .ndo_get_stats64 = nlmon_get_stats64, |
60 | }; |
61 | |
62 | static void nlmon_setup(struct net_device *dev) |
63 | { |
64 | dev->type = ARPHRD_NETLINK; |
65 | dev->priv_flags |= IFF_NO_QUEUE; |
66 | |
67 | dev->netdev_ops = &nlmon_ops; |
68 | dev->ethtool_ops = &nlmon_ethtool_ops; |
69 | dev->needs_free_netdev = true; |
70 | |
71 | dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | |
72 | NETIF_F_HIGHDMA | NETIF_F_LLTX; |
73 | dev->flags = IFF_NOARP; |
74 | dev->pcpu_stat_type = NETDEV_PCPU_STAT_LSTATS; |
75 | |
76 | /* That's rather a softlimit here, which, of course, |
77 | * can be altered. Not a real MTU, but what is to be |
78 | * expected in most cases. |
79 | */ |
80 | dev->mtu = NLMSG_GOODSIZE; |
81 | dev->min_mtu = sizeof(struct nlmsghdr); |
82 | } |
83 | |
84 | static int nlmon_validate(struct nlattr *tb[], struct nlattr *data[], |
85 | struct netlink_ext_ack *extack) |
86 | { |
87 | if (tb[IFLA_ADDRESS]) |
88 | return -EINVAL; |
89 | return 0; |
90 | } |
91 | |
92 | static struct rtnl_link_ops nlmon_link_ops __read_mostly = { |
93 | .kind = "nlmon" , |
94 | .priv_size = sizeof(struct nlmon), |
95 | .setup = nlmon_setup, |
96 | .validate = nlmon_validate, |
97 | }; |
98 | |
99 | static __init int nlmon_register(void) |
100 | { |
101 | return rtnl_link_register(ops: &nlmon_link_ops); |
102 | } |
103 | |
104 | static __exit void nlmon_unregister(void) |
105 | { |
106 | rtnl_link_unregister(ops: &nlmon_link_ops); |
107 | } |
108 | |
109 | module_init(nlmon_register); |
110 | module_exit(nlmon_unregister); |
111 | |
112 | MODULE_LICENSE("GPL v2" ); |
113 | MODULE_AUTHOR("Daniel Borkmann <dborkman@redhat.com>" ); |
114 | MODULE_AUTHOR("Mathieu Geli <geli@enseirb.fr>" ); |
115 | MODULE_DESCRIPTION("Netlink monitoring device" ); |
116 | MODULE_ALIAS_RTNL_LINK("nlmon" ); |
117 | |