| // SPDX-License-Identifier: GPL-2.0-or-later |
| /* Copyright Amazon.com Inc. or its affiliates. */ |
| |
| #include <linux/init.h> |
| #include <linux/netdevice.h> |
| #include <linux/notifier.h> |
| #include <linux/rtnetlink.h> |
| #include <net/net_namespace.h> |
| #include <net/netns/generic.h> |
| |
| static int rtnl_net_debug_event(struct notifier_block *nb, |
| unsigned long event, void *ptr) |
| { |
| struct net_device *dev = netdev_notifier_info_to_dev(ptr); |
| struct net *net = dev_net(dev); |
| enum netdev_cmd cmd = event; |
| |
| /* Keep enum and don't add default to trigger -Werror=switch */ |
| switch (cmd) { |
| case NETDEV_UP: |
| case NETDEV_DOWN: |
| case NETDEV_REBOOT: |
| case NETDEV_CHANGE: |
| case NETDEV_REGISTER: |
| case NETDEV_UNREGISTER: |
| case NETDEV_CHANGEMTU: |
| case NETDEV_CHANGEADDR: |
| case NETDEV_PRE_CHANGEADDR: |
| case NETDEV_GOING_DOWN: |
| case NETDEV_CHANGENAME: |
| case NETDEV_FEAT_CHANGE: |
| case NETDEV_BONDING_FAILOVER: |
| case NETDEV_PRE_UP: |
| case NETDEV_PRE_TYPE_CHANGE: |
| case NETDEV_POST_TYPE_CHANGE: |
| case NETDEV_POST_INIT: |
| case NETDEV_PRE_UNINIT: |
| case NETDEV_RELEASE: |
| case NETDEV_NOTIFY_PEERS: |
| case NETDEV_JOIN: |
| case NETDEV_CHANGEUPPER: |
| case NETDEV_RESEND_IGMP: |
| case NETDEV_PRECHANGEMTU: |
| case NETDEV_CHANGEINFODATA: |
| case NETDEV_BONDING_INFO: |
| case NETDEV_PRECHANGEUPPER: |
| case NETDEV_CHANGELOWERSTATE: |
| case NETDEV_UDP_TUNNEL_PUSH_INFO: |
| case NETDEV_UDP_TUNNEL_DROP_INFO: |
| case NETDEV_CHANGE_TX_QUEUE_LEN: |
| case NETDEV_CVLAN_FILTER_PUSH_INFO: |
| case NETDEV_CVLAN_FILTER_DROP_INFO: |
| case NETDEV_SVLAN_FILTER_PUSH_INFO: |
| case NETDEV_SVLAN_FILTER_DROP_INFO: |
| case NETDEV_OFFLOAD_XSTATS_ENABLE: |
| case NETDEV_OFFLOAD_XSTATS_DISABLE: |
| case NETDEV_OFFLOAD_XSTATS_REPORT_USED: |
| case NETDEV_OFFLOAD_XSTATS_REPORT_DELTA: |
| case NETDEV_XDP_FEAT_CHANGE: |
| ASSERT_RTNL(); |
| break; |
| |
| /* Once an event fully supports RTNL_NET, move it here |
| * and remove "if (0)" below. |
| * |
| * case NETDEV_XXX: |
| * ASSERT_RTNL_NET(net); |
| * break; |
| */ |
| } |
| |
| /* Just to avoid unused-variable error for dev and net. */ |
| if (0) |
| ASSERT_RTNL_NET(net); |
| |
| return NOTIFY_DONE; |
| } |
| |
| static int rtnl_net_debug_net_id; |
| |
| static int __net_init rtnl_net_debug_net_init(struct net *net) |
| { |
| struct notifier_block *nb; |
| |
| nb = net_generic(net, rtnl_net_debug_net_id); |
| nb->notifier_call = rtnl_net_debug_event; |
| |
| return register_netdevice_notifier_net(net, nb); |
| } |
| |
| static void __net_exit rtnl_net_debug_net_exit(struct net *net) |
| { |
| struct notifier_block *nb; |
| |
| nb = net_generic(net, rtnl_net_debug_net_id); |
| unregister_netdevice_notifier_net(net, nb); |
| } |
| |
| static struct pernet_operations rtnl_net_debug_net_ops __net_initdata = { |
| .init = rtnl_net_debug_net_init, |
| .exit = rtnl_net_debug_net_exit, |
| .id = &rtnl_net_debug_net_id, |
| .size = sizeof(struct notifier_block), |
| }; |
| |
| static struct notifier_block rtnl_net_debug_block = { |
| .notifier_call = rtnl_net_debug_event, |
| }; |
| |
| static int __init rtnl_net_debug_init(void) |
| { |
| int ret; |
| |
| ret = register_pernet_device(&rtnl_net_debug_net_ops); |
| if (ret) |
| return ret; |
| |
| ret = register_netdevice_notifier(&rtnl_net_debug_block); |
| if (ret) |
| unregister_pernet_subsys(&rtnl_net_debug_net_ops); |
| |
| return ret; |
| } |
| |
| static void __exit rtnl_net_debug_exit(void) |
| { |
| unregister_netdevice_notifier(&rtnl_net_debug_block); |
| unregister_pernet_device(&rtnl_net_debug_net_ops); |
| } |
| |
| subsys_initcall(rtnl_net_debug_init); |