linux/drivers/net/bonding/bond_ipv6.c
<<
>>
Prefs
   1/*
   2 * Copyright(c) 2008 Hewlett-Packard Development Company, L.P.
   3 *
   4 * This program is free software; you can redistribute it and/or modify it
   5 * under the terms of the GNU General Public License as published by the
   6 * Free Software Foundation; either version 2 of the License, or
   7 * (at your option) any later version.
   8 *
   9 * This program is distributed in the hope that it will be useful, but
  10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  11 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12 * for more details.
  13 *
  14 * You should have received a copy of the GNU General Public License along
  15 * with this program; if not, write to the Free Software Foundation, Inc.,
  16 * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  17 *
  18 * The full GNU General Public License is included in this distribution in the
  19 * file called LICENSE.
  20 *
  21 */
  22
  23#include <linux/types.h>
  24#include <linux/if_vlan.h>
  25#include <net/ipv6.h>
  26#include <net/ndisc.h>
  27#include <net/addrconf.h>
  28#include "bonding.h"
  29
  30/*
  31 * Assign bond->master_ipv6 to the next IPv6 address in the list, or
  32 * zero it out if there are none.
  33 */
  34static void bond_glean_dev_ipv6(struct net_device *dev, struct in6_addr *addr)
  35{
  36        struct inet6_dev *idev;
  37        struct inet6_ifaddr *ifa;
  38
  39        if (!dev)
  40                return;
  41
  42        idev = in6_dev_get(dev);
  43        if (!idev)
  44                return;
  45
  46        read_lock_bh(&idev->lock);
  47        ifa = idev->addr_list;
  48        if (ifa)
  49                ipv6_addr_copy(addr, &ifa->addr);
  50        else
  51                ipv6_addr_set(addr, 0, 0, 0, 0);
  52
  53        read_unlock_bh(&idev->lock);
  54
  55        in6_dev_put(idev);
  56}
  57
  58static void bond_na_send(struct net_device *slave_dev,
  59                         struct in6_addr *daddr,
  60                         int router,
  61                         unsigned short vlan_id)
  62{
  63        struct in6_addr mcaddr;
  64        struct icmp6hdr icmp6h = {
  65                .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
  66        };
  67        struct sk_buff *skb;
  68
  69        icmp6h.icmp6_router = router;
  70        icmp6h.icmp6_solicited = 0;
  71        icmp6h.icmp6_override = 1;
  72
  73        addrconf_addr_solict_mult(daddr, &mcaddr);
  74
  75        pr_debug("ipv6 na on slave %s: dest %pI6, src %pI6\n",
  76               slave_dev->name, &mcaddr, daddr);
  77
  78        skb = ndisc_build_skb(slave_dev, &mcaddr, daddr, &icmp6h, daddr,
  79                              ND_OPT_TARGET_LL_ADDR);
  80
  81        if (!skb) {
  82                pr_err(DRV_NAME ": NA packet allocation failed\n");
  83                return;
  84        }
  85
  86        if (vlan_id) {
  87                skb = vlan_put_tag(skb, vlan_id);
  88                if (!skb) {
  89                        pr_err(DRV_NAME ": failed to insert VLAN tag\n");
  90                        return;
  91                }
  92        }
  93
  94        ndisc_send_skb(skb, slave_dev, NULL, &mcaddr, daddr, &icmp6h);
  95}
  96
  97/*
  98 * Kick out an unsolicited Neighbor Advertisement for an IPv6 address on
  99 * the bonding master.  This will help the switch learn our address
 100 * if in active-backup mode.
 101 *
 102 * Caller must hold curr_slave_lock for read or better
 103 */
 104void bond_send_unsolicited_na(struct bonding *bond)
 105{
 106        struct slave *slave = bond->curr_active_slave;
 107        struct vlan_entry *vlan;
 108        struct inet6_dev *idev;
 109        int is_router;
 110
 111        pr_debug("bond_send_unsol_na: bond %s slave %s\n", bond->dev->name,
 112                                slave ? slave->dev->name : "NULL");
 113
 114        if (!slave || !bond->send_unsol_na ||
 115            test_bit(__LINK_STATE_LINKWATCH_PENDING, &slave->dev->state))
 116                return;
 117
 118        bond->send_unsol_na--;
 119
 120        idev = in6_dev_get(bond->dev);
 121        if (!idev)
 122                return;
 123
 124        is_router = !!idev->cnf.forwarding;
 125
 126        in6_dev_put(idev);
 127
 128        if (!ipv6_addr_any(&bond->master_ipv6))
 129                bond_na_send(slave->dev, &bond->master_ipv6, is_router, 0);
 130
 131        list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
 132                if (!ipv6_addr_any(&vlan->vlan_ipv6)) {
 133                        bond_na_send(slave->dev, &vlan->vlan_ipv6, is_router,
 134                                     vlan->vlan_id);
 135                }
 136        }
 137}
 138
 139/*
 140 * bond_inet6addr_event: handle inet6addr notifier chain events.
 141 *
 142 * We keep track of device IPv6 addresses primarily to use as source
 143 * addresses in NS probes.
 144 *
 145 * We track one IPv6 for the main device (if it has one).
 146 */
 147static int bond_inet6addr_event(struct notifier_block *this,
 148                                unsigned long event,
 149                                void *ptr)
 150{
 151        struct inet6_ifaddr *ifa = ptr;
 152        struct net_device *vlan_dev, *event_dev = ifa->idev->dev;
 153        struct bonding *bond;
 154        struct vlan_entry *vlan;
 155
 156        if (dev_net(event_dev) != &init_net)
 157                return NOTIFY_DONE;
 158
 159        list_for_each_entry(bond, &bond_dev_list, bond_list) {
 160                if (bond->dev == event_dev) {
 161                        switch (event) {
 162                        case NETDEV_UP:
 163                                if (ipv6_addr_any(&bond->master_ipv6))
 164                                        ipv6_addr_copy(&bond->master_ipv6,
 165                                                       &ifa->addr);
 166                                return NOTIFY_OK;
 167                        case NETDEV_DOWN:
 168                                if (ipv6_addr_equal(&bond->master_ipv6,
 169                                                    &ifa->addr))
 170                                        bond_glean_dev_ipv6(bond->dev,
 171                                                            &bond->master_ipv6);
 172                                return NOTIFY_OK;
 173                        default:
 174                                return NOTIFY_DONE;
 175                        }
 176                }
 177
 178                list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
 179                        vlan_dev = vlan_group_get_device(bond->vlgrp,
 180                                                         vlan->vlan_id);
 181                        if (vlan_dev == event_dev) {
 182                                switch (event) {
 183                                case NETDEV_UP:
 184                                        if (ipv6_addr_any(&vlan->vlan_ipv6))
 185                                                ipv6_addr_copy(&vlan->vlan_ipv6,
 186                                                               &ifa->addr);
 187                                        return NOTIFY_OK;
 188                                case NETDEV_DOWN:
 189                                        if (ipv6_addr_equal(&vlan->vlan_ipv6,
 190                                                            &ifa->addr))
 191                                                bond_glean_dev_ipv6(vlan_dev,
 192                                                                    &vlan->vlan_ipv6);
 193                                        return NOTIFY_OK;
 194                                default:
 195                                        return NOTIFY_DONE;
 196                                }
 197                        }
 198                }
 199        }
 200        return NOTIFY_DONE;
 201}
 202
 203static struct notifier_block bond_inet6addr_notifier = {
 204        .notifier_call = bond_inet6addr_event,
 205};
 206
 207void bond_register_ipv6_notifier(void)
 208{
 209        register_inet6addr_notifier(&bond_inet6addr_notifier);
 210}
 211
 212void bond_unregister_ipv6_notifier(void)
 213{
 214        unregister_inet6addr_notifier(&bond_inet6addr_notifier);
 215}
 216
 217