Revert "net/sched: flower: Fix wrong handle assignment during filter change"
[linux-block.git] / net / core / netdev-genl.c
1 // SPDX-License-Identifier: GPL-2.0-only
2
3 #include <linux/netdevice.h>
4 #include <linux/notifier.h>
5 #include <linux/rtnetlink.h>
6 #include <net/net_namespace.h>
7 #include <net/sock.h>
8
9 #include "netdev-genl-gen.h"
10
11 static int
12 netdev_nl_dev_fill(struct net_device *netdev, struct sk_buff *rsp,
13                    u32 portid, u32 seq, int flags, u32 cmd)
14 {
15         void *hdr;
16
17         hdr = genlmsg_put(rsp, portid, seq, &netdev_nl_family, flags, cmd);
18         if (!hdr)
19                 return -EMSGSIZE;
20
21         if (nla_put_u32(rsp, NETDEV_A_DEV_IFINDEX, netdev->ifindex) ||
22             nla_put_u64_64bit(rsp, NETDEV_A_DEV_XDP_FEATURES,
23                               netdev->xdp_features, NETDEV_A_DEV_PAD)) {
24                 genlmsg_cancel(rsp, hdr);
25                 return -EINVAL;
26         }
27
28         genlmsg_end(rsp, hdr);
29
30         return 0;
31 }
32
33 static void
34 netdev_genl_dev_notify(struct net_device *netdev, int cmd)
35 {
36         struct sk_buff *ntf;
37
38         if (!genl_has_listeners(&netdev_nl_family, dev_net(netdev),
39                                 NETDEV_NLGRP_MGMT))
40                 return;
41
42         ntf = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
43         if (!ntf)
44                 return;
45
46         if (netdev_nl_dev_fill(netdev, ntf, 0, 0, 0, cmd)) {
47                 nlmsg_free(ntf);
48                 return;
49         }
50
51         genlmsg_multicast_netns(&netdev_nl_family, dev_net(netdev), ntf,
52                                 0, NETDEV_NLGRP_MGMT, GFP_KERNEL);
53 }
54
55 int netdev_nl_dev_get_doit(struct sk_buff *skb, struct genl_info *info)
56 {
57         struct net_device *netdev;
58         struct sk_buff *rsp;
59         u32 ifindex;
60         int err;
61
62         if (GENL_REQ_ATTR_CHECK(info, NETDEV_A_DEV_IFINDEX))
63                 return -EINVAL;
64
65         ifindex = nla_get_u32(info->attrs[NETDEV_A_DEV_IFINDEX]);
66
67         rsp = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
68         if (!rsp)
69                 return -ENOMEM;
70
71         rtnl_lock();
72
73         netdev = __dev_get_by_index(genl_info_net(info), ifindex);
74         if (netdev)
75                 err = netdev_nl_dev_fill(netdev, rsp, info->snd_portid,
76                                          info->snd_seq, 0, info->genlhdr->cmd);
77         else
78                 err = -ENODEV;
79
80         rtnl_unlock();
81
82         if (err)
83                 goto err_free_msg;
84
85         return genlmsg_reply(rsp, info);
86
87 err_free_msg:
88         nlmsg_free(rsp);
89         return err;
90 }
91
92 int netdev_nl_dev_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
93 {
94         struct net *net = sock_net(skb->sk);
95         struct net_device *netdev;
96         int idx = 0, s_idx;
97         int h, s_h;
98         int err;
99
100         s_h = cb->args[0];
101         s_idx = cb->args[1];
102
103         rtnl_lock();
104
105         for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
106                 struct hlist_head *head;
107
108                 idx = 0;
109                 head = &net->dev_index_head[h];
110                 hlist_for_each_entry(netdev, head, index_hlist) {
111                         if (idx < s_idx)
112                                 goto cont;
113                         err = netdev_nl_dev_fill(netdev, skb,
114                                                  NETLINK_CB(cb->skb).portid,
115                                                  cb->nlh->nlmsg_seq, 0,
116                                                  NETDEV_CMD_DEV_GET);
117                         if (err < 0)
118                                 break;
119 cont:
120                         idx++;
121                 }
122         }
123
124         rtnl_unlock();
125
126         if (err != -EMSGSIZE)
127                 return err;
128
129         cb->args[1] = idx;
130         cb->args[0] = h;
131         cb->seq = net->dev_base_seq;
132
133         return skb->len;
134 }
135
136 static int netdev_genl_netdevice_event(struct notifier_block *nb,
137                                        unsigned long event, void *ptr)
138 {
139         struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
140
141         switch (event) {
142         case NETDEV_REGISTER:
143                 netdev_genl_dev_notify(netdev, NETDEV_CMD_DEV_ADD_NTF);
144                 break;
145         case NETDEV_UNREGISTER:
146                 netdev_genl_dev_notify(netdev, NETDEV_CMD_DEV_DEL_NTF);
147                 break;
148         case NETDEV_XDP_FEAT_CHANGE:
149                 netdev_genl_dev_notify(netdev, NETDEV_CMD_DEV_CHANGE_NTF);
150                 break;
151         }
152
153         return NOTIFY_OK;
154 }
155
156 static struct notifier_block netdev_genl_nb = {
157         .notifier_call  = netdev_genl_netdevice_event,
158 };
159
160 static int __init netdev_genl_init(void)
161 {
162         int err;
163
164         err = register_netdevice_notifier(&netdev_genl_nb);
165         if (err)
166                 return err;
167
168         err = genl_register_family(&netdev_nl_family);
169         if (err)
170                 goto err_unreg_ntf;
171
172         return 0;
173
174 err_unreg_ntf:
175         unregister_netdevice_notifier(&netdev_genl_nb);
176         return err;
177 }
178
179 subsys_initcall(netdev_genl_init);