aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/net/bonding/bond_netlink.c36
-rw-r--r--drivers/net/bonding/bond_options.c130
-rw-r--r--drivers/net/bonding/bond_sysfs.c84
-rw-r--r--drivers/net/bonding/bonding.h4
-rw-r--r--include/uapi/linux/if_link.h1
5 files changed, 187 insertions, 68 deletions
diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c
index 58e71235b75f..6d30e5db9e3c 100644
--- a/drivers/net/bonding/bond_netlink.c
+++ b/drivers/net/bonding/bond_netlink.c
@@ -29,6 +29,7 @@ static const struct nla_policy bond_policy[IFLA_BOND_MAX + 1] = {
29 [IFLA_BOND_DOWNDELAY] = { .type = NLA_U32 }, 29 [IFLA_BOND_DOWNDELAY] = { .type = NLA_U32 },
30 [IFLA_BOND_USE_CARRIER] = { .type = NLA_U8 }, 30 [IFLA_BOND_USE_CARRIER] = { .type = NLA_U8 },
31 [IFLA_BOND_ARP_INTERVAL] = { .type = NLA_U32 }, 31 [IFLA_BOND_ARP_INTERVAL] = { .type = NLA_U32 },
32 [IFLA_BOND_ARP_IP_TARGET] = { .type = NLA_NESTED },
32}; 33};
33 34
34static int bond_validate(struct nlattr *tb[], struct nlattr *data[]) 35static int bond_validate(struct nlattr *tb[], struct nlattr *data[])
@@ -116,6 +117,20 @@ static int bond_changelink(struct net_device *bond_dev,
116 if (err) 117 if (err)
117 return err; 118 return err;
118 } 119 }
120 if (data[IFLA_BOND_ARP_IP_TARGET]) {
121 __be32 targets[BOND_MAX_ARP_TARGETS] = { 0, };
122 struct nlattr *attr;
123 int i = 0, rem;
124
125 nla_for_each_nested(attr, data[IFLA_BOND_ARP_IP_TARGET], rem) {
126 __be32 target = nla_get_u32(attr);
127 targets[i++] = target;
128 }
129
130 err = bond_option_arp_ip_targets_set(bond, targets, i);
131 if (err)
132 return err;
133 }
119 return 0; 134 return 0;
120} 135}
121 136
@@ -140,6 +155,8 @@ static size_t bond_get_size(const struct net_device *bond_dev)
140 nla_total_size(sizeof(u32)) + /* IFLA_BOND_DOWNDELAY */ 155 nla_total_size(sizeof(u32)) + /* IFLA_BOND_DOWNDELAY */
141 nla_total_size(sizeof(u8)) + /* IFLA_BOND_USE_CARRIER */ 156 nla_total_size(sizeof(u8)) + /* IFLA_BOND_USE_CARRIER */
142 nla_total_size(sizeof(u32)) + /* IFLA_BOND_ARP_INTERVAL */ 157 nla_total_size(sizeof(u32)) + /* IFLA_BOND_ARP_INTERVAL */
158 /* IFLA_BOND_ARP_IP_TARGET */
159 nla_total_size(sizeof(u32)) * BOND_MAX_ARP_TARGETS +
143 0; 160 0;
144} 161}
145 162
@@ -148,6 +165,8 @@ static int bond_fill_info(struct sk_buff *skb,
148{ 165{
149 struct bonding *bond = netdev_priv(bond_dev); 166 struct bonding *bond = netdev_priv(bond_dev);
150 struct net_device *slave_dev = bond_option_active_slave_get(bond); 167 struct net_device *slave_dev = bond_option_active_slave_get(bond);
168 struct nlattr *targets;
169 int i, targets_added;
151 170
152 if (nla_put_u8(skb, IFLA_BOND_MODE, bond->params.mode)) 171 if (nla_put_u8(skb, IFLA_BOND_MODE, bond->params.mode))
153 goto nla_put_failure; 172 goto nla_put_failure;
@@ -173,6 +192,23 @@ static int bond_fill_info(struct sk_buff *skb,
173 if (nla_put_u32(skb, IFLA_BOND_ARP_INTERVAL, bond->params.arp_interval)) 192 if (nla_put_u32(skb, IFLA_BOND_ARP_INTERVAL, bond->params.arp_interval))
174 goto nla_put_failure; 193 goto nla_put_failure;
175 194
195 targets = nla_nest_start(skb, IFLA_BOND_ARP_IP_TARGET);
196 if (!targets)
197 goto nla_put_failure;
198
199 targets_added = 0;
200 for (i = 0; i < BOND_MAX_ARP_TARGETS; i++) {
201 if (bond->params.arp_targets[i]) {
202 nla_put_u32(skb, i, bond->params.arp_targets[i]);
203 targets_added = 1;
204 }
205 }
206
207 if (targets_added)
208 nla_nest_end(skb, targets);
209 else
210 nla_nest_cancel(skb, targets);
211
176 return 0; 212 return 0;
177 213
178nla_put_failure: 214nla_put_failure:
diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c
index a84e729d02ef..f8c2e4f17066 100644
--- a/drivers/net/bonding/bond_options.c
+++ b/drivers/net/bonding/bond_options.c
@@ -306,3 +306,133 @@ int bond_option_arp_interval_set(struct bonding *bond, int arp_interval)
306 306
307 return 0; 307 return 0;
308} 308}
309
310static void _bond_options_arp_ip_target_set(struct bonding *bond, int slot,
311 __be32 target,
312 unsigned long last_rx)
313{
314 __be32 *targets = bond->params.arp_targets;
315 struct list_head *iter;
316 struct slave *slave;
317
318 if (slot >= 0 && slot < BOND_MAX_ARP_TARGETS) {
319 bond_for_each_slave(bond, slave, iter)
320 slave->target_last_arp_rx[slot] = last_rx;
321 targets[slot] = target;
322 }
323}
324
325static int _bond_option_arp_ip_target_add(struct bonding *bond, __be32 target)
326{
327 __be32 *targets = bond->params.arp_targets;
328 int ind;
329
330 if (IS_IP_TARGET_UNUSABLE_ADDRESS(target)) {
331 pr_err("%s: invalid ARP target %pI4 specified for addition\n",
332 bond->dev->name, &target);
333 return -EINVAL;
334 }
335
336 if (bond_get_targets_ip(targets, target) != -1) { /* dup */
337 pr_err("%s: ARP target %pI4 is already present\n",
338 bond->dev->name, &target);
339 return -EINVAL;
340 }
341
342 ind = bond_get_targets_ip(targets, 0); /* first free slot */
343 if (ind == -1) {
344 pr_err("%s: ARP target table is full!\n",
345 bond->dev->name);
346 return -EINVAL;
347 }
348
349 pr_info("%s: adding ARP target %pI4.\n", bond->dev->name, &target);
350
351 _bond_options_arp_ip_target_set(bond, ind, target, jiffies);
352
353 return 0;
354}
355
356int bond_option_arp_ip_target_add(struct bonding *bond, __be32 target)
357{
358 int ret;
359
360 /* not to race with bond_arp_rcv */
361 write_lock_bh(&bond->lock);
362 ret = _bond_option_arp_ip_target_add(bond, target);
363 write_unlock_bh(&bond->lock);
364
365 return ret;
366}
367
368int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target)
369{
370 __be32 *targets = bond->params.arp_targets;
371 struct list_head *iter;
372 struct slave *slave;
373 unsigned long *targets_rx;
374 int ind, i;
375
376 if (IS_IP_TARGET_UNUSABLE_ADDRESS(target)) {
377 pr_err("%s: invalid ARP target %pI4 specified for removal\n",
378 bond->dev->name, &target);
379 return -EINVAL;
380 }
381
382 ind = bond_get_targets_ip(targets, target);
383 if (ind == -1) {
384 pr_err("%s: unable to remove nonexistent ARP target %pI4.\n",
385 bond->dev->name, &target);
386 return -EINVAL;
387 }
388
389 if (ind == 0 && !targets[1] && bond->params.arp_interval)
390 pr_warn("%s: removing last arp target with arp_interval on\n",
391 bond->dev->name);
392
393 pr_info("%s: removing ARP target %pI4.\n", bond->dev->name,
394 &target);
395
396 /* not to race with bond_arp_rcv */
397 write_lock_bh(&bond->lock);
398
399 bond_for_each_slave(bond, slave, iter) {
400 targets_rx = slave->target_last_arp_rx;
401 for (i = ind; (i < BOND_MAX_ARP_TARGETS-1) && targets[i+1]; i++)
402 targets_rx[i] = targets_rx[i+1];
403 targets_rx[i] = 0;
404 }
405 for (i = ind; (i < BOND_MAX_ARP_TARGETS-1) && targets[i+1]; i++)
406 targets[i] = targets[i+1];
407 targets[i] = 0;
408
409 write_unlock_bh(&bond->lock);
410
411 return 0;
412}
413
414int bond_option_arp_ip_targets_set(struct bonding *bond, __be32 *targets,
415 int count)
416{
417 int i, ret = 0;
418
419 /* not to race with bond_arp_rcv */
420 write_lock_bh(&bond->lock);
421
422 /* clear table */
423 for (i = 0; i < BOND_MAX_ARP_TARGETS; i++)
424 _bond_options_arp_ip_target_set(bond, i, 0, 0);
425
426 if (count == 0 && bond->params.arp_interval)
427 pr_warn("%s: removing last arp target with arp_interval on\n",
428 bond->dev->name);
429
430 for (i = 0; i < count; i++) {
431 ret = _bond_option_arp_ip_target_add(bond, targets[i]);
432 if (ret)
433 break;
434 }
435
436 write_unlock_bh(&bond->lock);
437 return ret;
438}
diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c
index 12128bfa88ce..a443bbd0fe86 100644
--- a/drivers/net/bonding/bond_sysfs.c
+++ b/drivers/net/bonding/bond_sysfs.c
@@ -552,81 +552,29 @@ static ssize_t bonding_store_arp_targets(struct device *d,
552 const char *buf, size_t count) 552 const char *buf, size_t count)
553{ 553{
554 struct bonding *bond = to_bond(d); 554 struct bonding *bond = to_bond(d);
555 struct list_head *iter; 555 __be32 target;
556 struct slave *slave; 556 int ret = -EPERM;
557 __be32 newtarget, *targets;
558 unsigned long *targets_rx;
559 int ind, i, j, ret = -EINVAL;
560 557
561 if (!rtnl_trylock()) 558 if (!in4_pton(buf + 1, -1, (u8 *)&target, -1, NULL)) {
562 return restart_syscall(); 559 pr_err("%s: invalid ARP target %pI4 specified\n",
563 560 bond->dev->name, &target);
564 targets = bond->params.arp_targets; 561 return -EPERM;
565 if (!in4_pton(buf + 1, -1, (u8 *)&newtarget, -1, NULL) ||
566 IS_IP_TARGET_UNUSABLE_ADDRESS(newtarget)) {
567 pr_err("%s: invalid ARP target %pI4 specified for addition\n",
568 bond->dev->name, &newtarget);
569 goto out;
570 } 562 }
571 /* look for adds */
572 if (buf[0] == '+') {
573 if (bond_get_targets_ip(targets, newtarget) != -1) { /* dup */
574 pr_err("%s: ARP target %pI4 is already present\n",
575 bond->dev->name, &newtarget);
576 goto out;
577 }
578
579 ind = bond_get_targets_ip(targets, 0); /* first free slot */
580 if (ind == -1) {
581 pr_err("%s: ARP target table is full!\n",
582 bond->dev->name);
583 goto out;
584 }
585
586 pr_info("%s: adding ARP target %pI4.\n", bond->dev->name,
587 &newtarget);
588 /* not to race with bond_arp_rcv */
589 write_lock_bh(&bond->lock);
590 bond_for_each_slave(bond, slave, iter)
591 slave->target_last_arp_rx[ind] = jiffies;
592 targets[ind] = newtarget;
593 write_unlock_bh(&bond->lock);
594 } else if (buf[0] == '-') {
595 ind = bond_get_targets_ip(targets, newtarget);
596 if (ind == -1) {
597 pr_err("%s: unable to remove nonexistent ARP target %pI4.\n",
598 bond->dev->name, &newtarget);
599 goto out;
600 }
601 563
602 if (ind == 0 && !targets[1] && bond->params.arp_interval) 564 if (!rtnl_trylock())
603 pr_warn("%s: removing last arp target with arp_interval on\n", 565 return restart_syscall();
604 bond->dev->name);
605
606 pr_info("%s: removing ARP target %pI4.\n", bond->dev->name,
607 &newtarget);
608 566
609 write_lock_bh(&bond->lock); 567 if (buf[0] == '+')
610 bond_for_each_slave(bond, slave, iter) { 568 ret = bond_option_arp_ip_target_add(bond, target);
611 targets_rx = slave->target_last_arp_rx; 569 else if (buf[0] == '-')
612 j = ind; 570 ret = bond_option_arp_ip_target_rem(bond, target);
613 for (; (j < BOND_MAX_ARP_TARGETS-1) && targets[j+1]; j++) 571 else
614 targets_rx[j] = targets_rx[j+1];
615 targets_rx[j] = 0;
616 }
617 for (i = ind; (i < BOND_MAX_ARP_TARGETS-1) && targets[i+1]; i++)
618 targets[i] = targets[i+1];
619 targets[i] = 0;
620 write_unlock_bh(&bond->lock);
621 } else {
622 pr_err("no command found in arp_ip_targets file for bond %s. Use +<addr> or -<addr>.\n", 572 pr_err("no command found in arp_ip_targets file for bond %s. Use +<addr> or -<addr>.\n",
623 bond->dev->name); 573 bond->dev->name);
624 ret = -EPERM;
625 goto out;
626 }
627 574
628 ret = count; 575 if (!ret)
629out: 576 ret = count;
577
630 rtnl_unlock(); 578 rtnl_unlock();
631 return ret; 579 return ret;
632} 580}
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index 44df2738577d..dcdc8095c169 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -444,6 +444,10 @@ int bond_option_updelay_set(struct bonding *bond, int updelay);
444int bond_option_downdelay_set(struct bonding *bond, int downdelay); 444int bond_option_downdelay_set(struct bonding *bond, int downdelay);
445int bond_option_use_carrier_set(struct bonding *bond, int use_carrier); 445int bond_option_use_carrier_set(struct bonding *bond, int use_carrier);
446int bond_option_arp_interval_set(struct bonding *bond, int arp_interval); 446int bond_option_arp_interval_set(struct bonding *bond, int arp_interval);
447int bond_option_arp_ip_targets_set(struct bonding *bond, __be32 *targets,
448 int count);
449int bond_option_arp_ip_target_add(struct bonding *bond, __be32 target);
450int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target);
447struct net_device *bond_option_active_slave_get_rcu(struct bonding *bond); 451struct net_device *bond_option_active_slave_get_rcu(struct bonding *bond);
448struct net_device *bond_option_active_slave_get(struct bonding *bond); 452struct net_device *bond_option_active_slave_get(struct bonding *bond);
449 453
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index c73d878afd58..5b0c44469279 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -336,6 +336,7 @@ enum {
336 IFLA_BOND_DOWNDELAY, 336 IFLA_BOND_DOWNDELAY,
337 IFLA_BOND_USE_CARRIER, 337 IFLA_BOND_USE_CARRIER,
338 IFLA_BOND_ARP_INTERVAL, 338 IFLA_BOND_ARP_INTERVAL,
339 IFLA_BOND_ARP_IP_TARGET,
339 __IFLA_BOND_MAX, 340 __IFLA_BOND_MAX,
340}; 341};
341 342