| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* Copyright (c) 2014 Mahesh Bandewar <maheshb@google.com> |
|---|
| 2 | | - * |
|---|
| 3 | | - * This program is free software; you can redistribute it and/or |
|---|
| 4 | | - * modify it under the terms of the GNU General Public License as |
|---|
| 5 | | - * published by the Free Software Foundation; either version 2 of |
|---|
| 6 | | - * the License, or (at your option) any later version. |
|---|
| 7 | | - * |
|---|
| 8 | 3 | */ |
|---|
| 9 | 4 | |
|---|
| 10 | 5 | #include "ipvlan.h" |
|---|
| 11 | 6 | |
|---|
| 12 | | -static unsigned int ipvlan_netid __read_mostly; |
|---|
| 13 | | - |
|---|
| 14 | | -struct ipvlan_netns { |
|---|
| 15 | | - unsigned int ipvl_nf_hook_refcnt; |
|---|
| 16 | | -}; |
|---|
| 17 | | - |
|---|
| 18 | | -static const struct nf_hook_ops ipvl_nfops[] = { |
|---|
| 19 | | - { |
|---|
| 20 | | - .hook = ipvlan_nf_input, |
|---|
| 21 | | - .pf = NFPROTO_IPV4, |
|---|
| 22 | | - .hooknum = NF_INET_LOCAL_IN, |
|---|
| 23 | | - .priority = INT_MAX, |
|---|
| 24 | | - }, |
|---|
| 25 | | -#if IS_ENABLED(CONFIG_IPV6) |
|---|
| 26 | | - { |
|---|
| 27 | | - .hook = ipvlan_nf_input, |
|---|
| 28 | | - .pf = NFPROTO_IPV6, |
|---|
| 29 | | - .hooknum = NF_INET_LOCAL_IN, |
|---|
| 30 | | - .priority = INT_MAX, |
|---|
| 31 | | - }, |
|---|
| 32 | | -#endif |
|---|
| 33 | | -}; |
|---|
| 34 | | - |
|---|
| 35 | | -static const struct l3mdev_ops ipvl_l3mdev_ops = { |
|---|
| 36 | | - .l3mdev_l3_rcv = ipvlan_l3_rcv, |
|---|
| 37 | | -}; |
|---|
| 38 | | - |
|---|
| 39 | | -static void ipvlan_adjust_mtu(struct ipvl_dev *ipvlan, struct net_device *dev) |
|---|
| 40 | | -{ |
|---|
| 41 | | - ipvlan->dev->mtu = dev->mtu; |
|---|
| 42 | | -} |
|---|
| 43 | | - |
|---|
| 44 | | -static int ipvlan_register_nf_hook(struct net *net) |
|---|
| 45 | | -{ |
|---|
| 46 | | - struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid); |
|---|
| 47 | | - int err = 0; |
|---|
| 48 | | - |
|---|
| 49 | | - if (!vnet->ipvl_nf_hook_refcnt) { |
|---|
| 50 | | - err = nf_register_net_hooks(net, ipvl_nfops, |
|---|
| 51 | | - ARRAY_SIZE(ipvl_nfops)); |
|---|
| 52 | | - if (!err) |
|---|
| 53 | | - vnet->ipvl_nf_hook_refcnt = 1; |
|---|
| 54 | | - } else { |
|---|
| 55 | | - vnet->ipvl_nf_hook_refcnt++; |
|---|
| 56 | | - } |
|---|
| 57 | | - |
|---|
| 58 | | - return err; |
|---|
| 59 | | -} |
|---|
| 60 | | - |
|---|
| 61 | | -static void ipvlan_unregister_nf_hook(struct net *net) |
|---|
| 62 | | -{ |
|---|
| 63 | | - struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid); |
|---|
| 64 | | - |
|---|
| 65 | | - if (WARN_ON(!vnet->ipvl_nf_hook_refcnt)) |
|---|
| 66 | | - return; |
|---|
| 67 | | - |
|---|
| 68 | | - vnet->ipvl_nf_hook_refcnt--; |
|---|
| 69 | | - if (!vnet->ipvl_nf_hook_refcnt) |
|---|
| 70 | | - nf_unregister_net_hooks(net, ipvl_nfops, |
|---|
| 71 | | - ARRAY_SIZE(ipvl_nfops)); |
|---|
| 72 | | -} |
|---|
| 73 | | - |
|---|
| 74 | | -static int ipvlan_set_port_mode(struct ipvl_port *port, u16 nval) |
|---|
| 7 | +static int ipvlan_set_port_mode(struct ipvl_port *port, u16 nval, |
|---|
| 8 | + struct netlink_ext_ack *extack) |
|---|
| 75 | 9 | { |
|---|
| 76 | 10 | struct ipvl_dev *ipvlan; |
|---|
| 77 | | - struct net_device *mdev = port->dev; |
|---|
| 78 | 11 | unsigned int flags; |
|---|
| 79 | 12 | int err; |
|---|
| 80 | 13 | |
|---|
| .. | .. |
|---|
| 84 | 17 | flags = ipvlan->dev->flags; |
|---|
| 85 | 18 | if (nval == IPVLAN_MODE_L3 || nval == IPVLAN_MODE_L3S) { |
|---|
| 86 | 19 | err = dev_change_flags(ipvlan->dev, |
|---|
| 87 | | - flags | IFF_NOARP); |
|---|
| 20 | + flags | IFF_NOARP, |
|---|
| 21 | + extack); |
|---|
| 88 | 22 | } else { |
|---|
| 89 | 23 | err = dev_change_flags(ipvlan->dev, |
|---|
| 90 | | - flags & ~IFF_NOARP); |
|---|
| 24 | + flags & ~IFF_NOARP, |
|---|
| 25 | + extack); |
|---|
| 91 | 26 | } |
|---|
| 92 | 27 | if (unlikely(err)) |
|---|
| 93 | 28 | goto fail; |
|---|
| 94 | 29 | } |
|---|
| 95 | 30 | if (nval == IPVLAN_MODE_L3S) { |
|---|
| 96 | 31 | /* New mode is L3S */ |
|---|
| 97 | | - err = ipvlan_register_nf_hook(read_pnet(&port->pnet)); |
|---|
| 98 | | - if (!err) { |
|---|
| 99 | | - mdev->l3mdev_ops = &ipvl_l3mdev_ops; |
|---|
| 100 | | - mdev->priv_flags |= IFF_L3MDEV_RX_HANDLER; |
|---|
| 101 | | - } else |
|---|
| 32 | + err = ipvlan_l3s_register(port); |
|---|
| 33 | + if (err) |
|---|
| 102 | 34 | goto fail; |
|---|
| 103 | 35 | } else if (port->mode == IPVLAN_MODE_L3S) { |
|---|
| 104 | 36 | /* Old mode was L3S */ |
|---|
| 105 | | - mdev->priv_flags &= ~IFF_L3MDEV_RX_HANDLER; |
|---|
| 106 | | - ipvlan_unregister_nf_hook(read_pnet(&port->pnet)); |
|---|
| 107 | | - mdev->l3mdev_ops = NULL; |
|---|
| 37 | + ipvlan_l3s_unregister(port); |
|---|
| 108 | 38 | } |
|---|
| 109 | 39 | port->mode = nval; |
|---|
| 110 | 40 | } |
|---|
| .. | .. |
|---|
| 116 | 46 | flags = ipvlan->dev->flags; |
|---|
| 117 | 47 | if (port->mode == IPVLAN_MODE_L3 || |
|---|
| 118 | 48 | port->mode == IPVLAN_MODE_L3S) |
|---|
| 119 | | - dev_change_flags(ipvlan->dev, flags | IFF_NOARP); |
|---|
| 49 | + dev_change_flags(ipvlan->dev, flags | IFF_NOARP, |
|---|
| 50 | + NULL); |
|---|
| 120 | 51 | else |
|---|
| 121 | | - dev_change_flags(ipvlan->dev, flags & ~IFF_NOARP); |
|---|
| 52 | + dev_change_flags(ipvlan->dev, flags & ~IFF_NOARP, |
|---|
| 53 | + NULL); |
|---|
| 122 | 54 | } |
|---|
| 123 | 55 | |
|---|
| 124 | 56 | return err; |
|---|
| .. | .. |
|---|
| 161 | 93 | struct ipvl_port *port = ipvlan_port_get_rtnl(dev); |
|---|
| 162 | 94 | struct sk_buff *skb; |
|---|
| 163 | 95 | |
|---|
| 164 | | - if (port->mode == IPVLAN_MODE_L3S) { |
|---|
| 165 | | - dev->priv_flags &= ~IFF_L3MDEV_RX_HANDLER; |
|---|
| 166 | | - ipvlan_unregister_nf_hook(dev_net(dev)); |
|---|
| 167 | | - dev->l3mdev_ops = NULL; |
|---|
| 168 | | - } |
|---|
| 96 | + if (port->mode == IPVLAN_MODE_L3S) |
|---|
| 97 | + ipvlan_l3s_unregister(port); |
|---|
| 169 | 98 | netdev_rx_handler_unregister(dev); |
|---|
| 170 | 99 | cancel_work_sync(&port->wq); |
|---|
| 171 | 100 | while ((skb = __skb_dequeue(&port->backlog)) != NULL) { |
|---|
| .. | .. |
|---|
| 186 | 115 | |
|---|
| 187 | 116 | #define IPVLAN_FEATURES \ |
|---|
| 188 | 117 | (NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | \ |
|---|
| 189 | | - NETIF_F_GSO | NETIF_F_TSO | NETIF_F_GSO_ROBUST | \ |
|---|
| 190 | | - NETIF_F_TSO_ECN | NETIF_F_TSO6 | NETIF_F_GRO | NETIF_F_RXCSUM | \ |
|---|
| 118 | + NETIF_F_GSO | NETIF_F_ALL_TSO | NETIF_F_GSO_ROBUST | \ |
|---|
| 119 | + NETIF_F_GRO | NETIF_F_RXCSUM | \ |
|---|
| 191 | 120 | NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_STAG_FILTER) |
|---|
| 192 | 121 | |
|---|
| 193 | 122 | /* NETIF_F_GSO_ENCAP_ALL NETIF_F_GSO_SOFTWARE Newly added */ |
|---|
| .. | .. |
|---|
| 208 | 137 | dev->features |= IPVLAN_ALWAYS_ON; |
|---|
| 209 | 138 | dev->vlan_features = phy_dev->vlan_features & IPVLAN_FEATURES; |
|---|
| 210 | 139 | dev->vlan_features |= IPVLAN_ALWAYS_ON_OFLOADS; |
|---|
| 140 | + dev->hw_enc_features |= dev->features; |
|---|
| 211 | 141 | dev->gso_max_size = phy_dev->gso_max_size; |
|---|
| 212 | 142 | dev->gso_max_segs = phy_dev->gso_max_segs; |
|---|
| 213 | 143 | dev->hard_header_len = phy_dev->hard_header_len; |
|---|
| .. | .. |
|---|
| 456 | 386 | .cache_update = eth_header_cache_update, |
|---|
| 457 | 387 | }; |
|---|
| 458 | 388 | |
|---|
| 389 | +static void ipvlan_adjust_mtu(struct ipvl_dev *ipvlan, struct net_device *dev) |
|---|
| 390 | +{ |
|---|
| 391 | + ipvlan->dev->mtu = dev->mtu; |
|---|
| 392 | +} |
|---|
| 393 | + |
|---|
| 459 | 394 | static bool netif_is_ipvlan(const struct net_device *dev) |
|---|
| 460 | 395 | { |
|---|
| 461 | 396 | /* both ipvlan and ipvtap devices use the same netdev_ops */ |
|---|
| .. | .. |
|---|
| 515 | 450 | if (data[IFLA_IPVLAN_MODE]) { |
|---|
| 516 | 451 | u16 nmode = nla_get_u16(data[IFLA_IPVLAN_MODE]); |
|---|
| 517 | 452 | |
|---|
| 518 | | - err = ipvlan_set_port_mode(port, nmode); |
|---|
| 453 | + err = ipvlan_set_port_mode(port, nmode, extack); |
|---|
| 519 | 454 | } |
|---|
| 520 | 455 | |
|---|
| 521 | 456 | if (!err && data[IFLA_IPVLAN_FLAGS]) { |
|---|
| .. | .. |
|---|
| 552 | 487 | if (data[IFLA_IPVLAN_MODE]) { |
|---|
| 553 | 488 | u16 mode = nla_get_u16(data[IFLA_IPVLAN_MODE]); |
|---|
| 554 | 489 | |
|---|
| 555 | | - if (mode < IPVLAN_MODE_L2 || mode >= IPVLAN_MODE_MAX) |
|---|
| 490 | + if (mode >= IPVLAN_MODE_MAX) |
|---|
| 556 | 491 | return -EINVAL; |
|---|
| 557 | 492 | } |
|---|
| 558 | 493 | if (data[IFLA_IPVLAN_FLAGS]) { |
|---|
| .. | .. |
|---|
| 691 | 626 | if (data && data[IFLA_IPVLAN_MODE]) |
|---|
| 692 | 627 | mode = nla_get_u16(data[IFLA_IPVLAN_MODE]); |
|---|
| 693 | 628 | |
|---|
| 694 | | - err = ipvlan_set_port_mode(port, mode); |
|---|
| 629 | + err = ipvlan_set_port_mode(port, mode, extack); |
|---|
| 695 | 630 | if (err) |
|---|
| 696 | 631 | goto unlink_netdev; |
|---|
| 697 | 632 | |
|---|
| .. | .. |
|---|
| 749 | 684 | [IFLA_IPVLAN_FLAGS] = { .type = NLA_U16 }, |
|---|
| 750 | 685 | }; |
|---|
| 751 | 686 | |
|---|
| 687 | +static struct net *ipvlan_get_link_net(const struct net_device *dev) |
|---|
| 688 | +{ |
|---|
| 689 | + struct ipvl_dev *ipvlan = netdev_priv(dev); |
|---|
| 690 | + |
|---|
| 691 | + return dev_net(ipvlan->phy_dev); |
|---|
| 692 | +} |
|---|
| 693 | + |
|---|
| 752 | 694 | static struct rtnl_link_ops ipvlan_link_ops = { |
|---|
| 753 | 695 | .kind = "ipvlan", |
|---|
| 754 | 696 | .priv_size = sizeof(struct ipvl_dev), |
|---|
| .. | .. |
|---|
| 756 | 698 | .setup = ipvlan_link_setup, |
|---|
| 757 | 699 | .newlink = ipvlan_link_new, |
|---|
| 758 | 700 | .dellink = ipvlan_link_delete, |
|---|
| 701 | + .get_link_net = ipvlan_get_link_net, |
|---|
| 759 | 702 | }; |
|---|
| 760 | 703 | |
|---|
| 761 | 704 | int ipvlan_link_register(struct rtnl_link_ops *ops) |
|---|
| .. | .. |
|---|
| 773 | 716 | static int ipvlan_device_event(struct notifier_block *unused, |
|---|
| 774 | 717 | unsigned long event, void *ptr) |
|---|
| 775 | 718 | { |
|---|
| 719 | + struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(ptr); |
|---|
| 720 | + struct netdev_notifier_pre_changeaddr_info *prechaddr_info; |
|---|
| 776 | 721 | struct net_device *dev = netdev_notifier_info_to_dev(ptr); |
|---|
| 777 | 722 | struct ipvl_dev *ipvlan, *next; |
|---|
| 778 | 723 | struct ipvl_port *port; |
|---|
| 779 | 724 | LIST_HEAD(lst_kill); |
|---|
| 725 | + int err; |
|---|
| 780 | 726 | |
|---|
| 781 | 727 | if (!netif_is_ipvlan_port(dev)) |
|---|
| 782 | 728 | return NOTIFY_DONE; |
|---|
| .. | .. |
|---|
| 792 | 738 | |
|---|
| 793 | 739 | case NETDEV_REGISTER: { |
|---|
| 794 | 740 | struct net *oldnet, *newnet = dev_net(dev); |
|---|
| 795 | | - struct ipvlan_netns *old_vnet; |
|---|
| 796 | 741 | |
|---|
| 797 | 742 | oldnet = read_pnet(&port->pnet); |
|---|
| 798 | 743 | if (net_eq(newnet, oldnet)) |
|---|
| .. | .. |
|---|
| 800 | 745 | |
|---|
| 801 | 746 | write_pnet(&port->pnet, newnet); |
|---|
| 802 | 747 | |
|---|
| 803 | | - old_vnet = net_generic(oldnet, ipvlan_netid); |
|---|
| 804 | | - if (!old_vnet->ipvl_nf_hook_refcnt) |
|---|
| 805 | | - break; |
|---|
| 806 | | - |
|---|
| 807 | | - ipvlan_register_nf_hook(newnet); |
|---|
| 808 | | - ipvlan_unregister_nf_hook(oldnet); |
|---|
| 748 | + ipvlan_migrate_l3s_hook(oldnet, newnet); |
|---|
| 809 | 749 | break; |
|---|
| 810 | 750 | } |
|---|
| 811 | 751 | case NETDEV_UNREGISTER: |
|---|
| .. | .. |
|---|
| 829 | 769 | case NETDEV_CHANGEMTU: |
|---|
| 830 | 770 | list_for_each_entry(ipvlan, &port->ipvlans, pnode) |
|---|
| 831 | 771 | ipvlan_adjust_mtu(ipvlan, dev); |
|---|
| 772 | + break; |
|---|
| 773 | + |
|---|
| 774 | + case NETDEV_PRE_CHANGEADDR: |
|---|
| 775 | + prechaddr_info = ptr; |
|---|
| 776 | + list_for_each_entry(ipvlan, &port->ipvlans, pnode) { |
|---|
| 777 | + err = dev_pre_changeaddr_notify(ipvlan->dev, |
|---|
| 778 | + prechaddr_info->dev_addr, |
|---|
| 779 | + extack); |
|---|
| 780 | + if (err) |
|---|
| 781 | + return notifier_from_errno(err); |
|---|
| 782 | + } |
|---|
| 832 | 783 | break; |
|---|
| 833 | 784 | |
|---|
| 834 | 785 | case NETDEV_CHANGEADDR: |
|---|
| .. | .. |
|---|
| 1067 | 1018 | }; |
|---|
| 1068 | 1019 | #endif |
|---|
| 1069 | 1020 | |
|---|
| 1070 | | -static void ipvlan_ns_exit(struct net *net) |
|---|
| 1071 | | -{ |
|---|
| 1072 | | - struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid); |
|---|
| 1073 | | - |
|---|
| 1074 | | - if (WARN_ON_ONCE(vnet->ipvl_nf_hook_refcnt)) { |
|---|
| 1075 | | - vnet->ipvl_nf_hook_refcnt = 0; |
|---|
| 1076 | | - nf_unregister_net_hooks(net, ipvl_nfops, |
|---|
| 1077 | | - ARRAY_SIZE(ipvl_nfops)); |
|---|
| 1078 | | - } |
|---|
| 1079 | | -} |
|---|
| 1080 | | - |
|---|
| 1081 | | -static struct pernet_operations ipvlan_net_ops = { |
|---|
| 1082 | | - .id = &ipvlan_netid, |
|---|
| 1083 | | - .size = sizeof(struct ipvlan_netns), |
|---|
| 1084 | | - .exit = ipvlan_ns_exit, |
|---|
| 1085 | | -}; |
|---|
| 1086 | | - |
|---|
| 1087 | 1021 | static int __init ipvlan_init_module(void) |
|---|
| 1088 | 1022 | { |
|---|
| 1089 | 1023 | int err; |
|---|
| .. | .. |
|---|
| 1098 | 1032 | register_inetaddr_notifier(&ipvlan_addr4_notifier_block); |
|---|
| 1099 | 1033 | register_inetaddr_validator_notifier(&ipvlan_addr4_vtor_notifier_block); |
|---|
| 1100 | 1034 | |
|---|
| 1101 | | - err = register_pernet_subsys(&ipvlan_net_ops); |
|---|
| 1035 | + err = ipvlan_l3s_init(); |
|---|
| 1102 | 1036 | if (err < 0) |
|---|
| 1103 | 1037 | goto error; |
|---|
| 1104 | 1038 | |
|---|
| 1105 | 1039 | err = ipvlan_link_register(&ipvlan_link_ops); |
|---|
| 1106 | 1040 | if (err < 0) { |
|---|
| 1107 | | - unregister_pernet_subsys(&ipvlan_net_ops); |
|---|
| 1041 | + ipvlan_l3s_cleanup(); |
|---|
| 1108 | 1042 | goto error; |
|---|
| 1109 | 1043 | } |
|---|
| 1110 | 1044 | |
|---|
| .. | .. |
|---|
| 1125 | 1059 | static void __exit ipvlan_cleanup_module(void) |
|---|
| 1126 | 1060 | { |
|---|
| 1127 | 1061 | rtnl_link_unregister(&ipvlan_link_ops); |
|---|
| 1128 | | - unregister_pernet_subsys(&ipvlan_net_ops); |
|---|
| 1062 | + ipvlan_l3s_cleanup(); |
|---|
| 1129 | 1063 | unregister_netdevice_notifier(&ipvlan_notifier_block); |
|---|
| 1130 | 1064 | unregister_inetaddr_notifier(&ipvlan_addr4_notifier_block); |
|---|
| 1131 | 1065 | unregister_inetaddr_validator_notifier( |
|---|