1/* 2 * IEEE 802.1D Generic Attribute Registration Protocol (GARP) 3 * 4 * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * version 2 as published by the Free Software Foundation. 9 */ 10#include <linux/kernel.h> 11#include <linux/timer.h> 12#include <linux/skbuff.h> 13#include <linux/netdevice.h> 14#include <linux/etherdevice.h> 15#include <linux/rtnetlink.h> 16#include <linux/llc.h> 17#include <linux/slab.h> 18#include <linux/module.h> 19#include <net/llc.h> 20#include <net/llc_pdu.h> 21#include <net/garp.h> 22#include <asm/unaligned.h> 23 24static unsigned int garp_join_time __read_mostly = 200; 25module_param(garp_join_time, uint, 0644); 26MODULE_PARM_DESC(garp_join_time, "Join time in ms (default 200ms)"); 27MODULE_LICENSE("GPL"); 28 29static const struct garp_state_trans { 30 u8 state; 31 u8 action; 32} garp_applicant_state_table[GARP_APPLICANT_MAX + 1][GARP_EVENT_MAX + 1] = { 33 [GARP_APPLICANT_VA] = { 34 [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_AA, 35 .action = GARP_ACTION_S_JOIN_IN }, 36 [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_AA }, 37 [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VA }, 38 [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VA }, 39 [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VA }, 40 [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, 41 [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, 42 [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_LA }, 43 }, 44 [GARP_APPLICANT_AA] = { 45 [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_QA, 46 .action = GARP_ACTION_S_JOIN_IN }, 47 [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QA }, 48 [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VA }, 49 [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VA }, 50 [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VA }, 51 [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, 52 [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, 53 [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_LA }, 54 }, 55 [GARP_APPLICANT_QA] = { 56 [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID }, 57 [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QA }, 58 [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VA }, 59 [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VA }, 60 [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP }, 61 [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, 62 [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, 63 [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_LA }, 64 }, 65 [GARP_APPLICANT_LA] = { 66 [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_VO, 67 .action = GARP_ACTION_S_LEAVE_EMPTY }, 68 [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_LA }, 69 [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO }, 70 [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_LA }, 71 [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_LA }, 72 [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO }, 73 [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_VA }, 74 [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID }, 75 }, 76 [GARP_APPLICANT_VP] = { 77 [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_AA, 78 .action = GARP_ACTION_S_JOIN_IN }, 79 [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_AP }, 80 [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VP }, 81 [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VP }, 82 [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP }, 83 [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, 84 [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, 85 [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_VO }, 86 }, 87 [GARP_APPLICANT_AP] = { 88 [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_QA, 89 .action = GARP_ACTION_S_JOIN_IN }, 90 [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QP }, 91 [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VP }, 92 [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VP }, 93 [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP }, 94 [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, 95 [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, 96 [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_AO }, 97 }, 98 [GARP_APPLICANT_QP] = { 99 [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID }, 100 [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QP }, 101 [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VP }, 102 [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VP }, 103 [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP }, 104 [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, 105 [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, 106 [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_QO }, 107 }, 108 [GARP_APPLICANT_VO] = { 109 [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID }, 110 [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_AO }, 111 [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO }, 112 [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VO }, 113 [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VO }, 114 [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO }, 115 [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_VP }, 116 [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID }, 117 }, 118 [GARP_APPLICANT_AO] = { 119 [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID }, 120 [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QO }, 121 [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO }, 122 [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VO }, 123 [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VO }, 124 [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO }, 125 [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_AP }, 126 [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID }, 127 }, 128 [GARP_APPLICANT_QO] = { 129 [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID }, 130 [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QO }, 131 [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO }, 132 [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VO }, 133 [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VO }, 134 [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO }, 135 [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_QP }, 136 [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID }, 137 }, 138}; 139 140static int garp_attr_cmp(const struct garp_attr *attr, 141 const void *data, u8 len, u8 type) 142{ 143 if (attr->type != type) 144 return attr->type - type; 145 if (attr->dlen != len) 146 return attr->dlen - len; 147 return memcmp(attr->data, data, len); 148} 149 150static struct garp_attr *garp_attr_lookup(const struct garp_applicant *app, 151 const void *data, u8 len, u8 type) 152{ 153 struct rb_node *parent = app->gid.rb_node; 154 struct garp_attr *attr; 155 int d; 156 157 while (parent) { 158 attr = rb_entry(parent, struct garp_attr, node); 159 d = garp_attr_cmp(attr, data, len, type); 160 if (d > 0) 161 parent = parent->rb_left; 162 else if (d < 0) 163 parent = parent->rb_right; 164 else 165 return attr; 166 } 167 return NULL; 168} 169 170static struct garp_attr *garp_attr_create(struct garp_applicant *app, 171 const void *data, u8 len, u8 type) 172{ 173 struct rb_node *parent = NULL, **p = &app->gid.rb_node; 174 struct garp_attr *attr; 175 int d; 176 177 while (*p) { 178 parent = *p; 179 attr = rb_entry(parent, struct garp_attr, node); 180 d = garp_attr_cmp(attr, data, len, type); 181 if (d > 0) 182 p = &parent->rb_left; 183 else if (d < 0) 184 p = &parent->rb_right; 185 else { 186 /* The attribute already exists; re-use it. */ 187 return attr; 188 } 189 } 190 attr = kmalloc(sizeof(*attr) + len, GFP_ATOMIC); 191 if (!attr) 192 return attr; 193 attr->state = GARP_APPLICANT_VO; 194 attr->type = type; 195 attr->dlen = len; 196 memcpy(attr->data, data, len); 197 198 rb_link_node(&attr->node, parent, p); 199 rb_insert_color(&attr->node, &app->gid); 200 return attr; 201} 202 203static void garp_attr_destroy(struct garp_applicant *app, struct garp_attr *attr) 204{ 205 rb_erase(&attr->node, &app->gid); 206 kfree(attr); 207} 208 209static int garp_pdu_init(struct garp_applicant *app) 210{ 211 struct sk_buff *skb; 212 struct garp_pdu_hdr *gp; 213 214#define LLC_RESERVE sizeof(struct llc_pdu_un) 215 skb = alloc_skb(app->dev->mtu + LL_RESERVED_SPACE(app->dev), 216 GFP_ATOMIC); 217 if (!skb) 218 return -ENOMEM; 219 220 skb->dev = app->dev; 221 skb->protocol = htons(ETH_P_802_2); 222 skb_reserve(skb, LL_RESERVED_SPACE(app->dev) + LLC_RESERVE); 223 224 gp = (struct garp_pdu_hdr *)__skb_put(skb, sizeof(*gp)); 225 put_unaligned(htons(GARP_PROTOCOL_ID), &gp->protocol); 226 227 app->pdu = skb; 228 return 0; 229} 230 231static int garp_pdu_append_end_mark(struct garp_applicant *app) 232{ 233 if (skb_tailroom(app->pdu) < sizeof(u8)) 234 return -1; 235 *(u8 *)__skb_put(app->pdu, sizeof(u8)) = GARP_END_MARK; 236 return 0; 237} 238 239static void garp_pdu_queue(struct garp_applicant *app) 240{ 241 if (!app->pdu) 242 return; 243 244 garp_pdu_append_end_mark(app); 245 garp_pdu_append_end_mark(app); 246 247 llc_pdu_header_init(app->pdu, LLC_PDU_TYPE_U, LLC_SAP_BSPAN, 248 LLC_SAP_BSPAN, LLC_PDU_CMD); 249 llc_pdu_init_as_ui_cmd(app->pdu); 250 llc_mac_hdr_init(app->pdu, app->dev->dev_addr, 251 app->app->proto.group_address); 252 253 skb_queue_tail(&app->queue, app->pdu); 254 app->pdu = NULL; 255} 256 257static void garp_queue_xmit(struct garp_applicant *app) 258{ 259 struct sk_buff *skb; 260 261 while ((skb = skb_dequeue(&app->queue))) 262 dev_queue_xmit(skb); 263} 264 265static int garp_pdu_append_msg(struct garp_applicant *app, u8 attrtype) 266{ 267 struct garp_msg_hdr *gm; 268 269 if (skb_tailroom(app->pdu) < sizeof(*gm)) 270 return -1; 271 gm = (struct garp_msg_hdr *)__skb_put(app->pdu, sizeof(*gm)); 272 gm->attrtype = attrtype; 273 garp_cb(app->pdu)->cur_type = attrtype; 274 return 0; 275} 276 277static int garp_pdu_append_attr(struct garp_applicant *app, 278 const struct garp_attr *attr, 279 enum garp_attr_event event) 280{ 281 struct garp_attr_hdr *ga; 282 unsigned int len; 283 int err; 284again: 285 if (!app->pdu) { 286 err = garp_pdu_init(app); 287 if (err < 0) 288 return err; 289 } 290 291 if (garp_cb(app->pdu)->cur_type != attr->type) { 292 if (garp_cb(app->pdu)->cur_type && 293 garp_pdu_append_end_mark(app) < 0) 294 goto queue; 295 if (garp_pdu_append_msg(app, attr->type) < 0) 296 goto queue; 297 } 298 299 len = sizeof(*ga) + attr->dlen; 300 if (skb_tailroom(app->pdu) < len) 301 goto queue; 302 ga = (struct garp_attr_hdr *)__skb_put(app->pdu, len); 303 ga->len = len; 304 ga->event = event; 305 memcpy(ga->data, attr->data, attr->dlen); 306 return 0; 307 308queue: 309 garp_pdu_queue(app); 310 goto again; 311} 312 313static void garp_attr_event(struct garp_applicant *app, 314 struct garp_attr *attr, enum garp_event event) 315{ 316 enum garp_applicant_state state; 317 318 state = garp_applicant_state_table[attr->state][event].state; 319 if (state == GARP_APPLICANT_INVALID) 320 return; 321 322 switch (garp_applicant_state_table[attr->state][event].action) { 323 case GARP_ACTION_NONE: 324 break; 325 case GARP_ACTION_S_JOIN_IN: 326 /* When appending the attribute fails, don't update state in 327 * order to retry on next TRANSMIT_PDU event. */ 328 if (garp_pdu_append_attr(app, attr, GARP_JOIN_IN) < 0) 329 return; 330 break; 331 case GARP_ACTION_S_LEAVE_EMPTY: 332 garp_pdu_append_attr(app, attr, GARP_LEAVE_EMPTY); 333 /* As a pure applicant, sending a leave message implies that 334 * the attribute was unregistered and can be destroyed. */ 335 garp_attr_destroy(app, attr); 336 return; 337 default: 338 WARN_ON(1); 339 } 340 341 attr->state = state; 342} 343 344int garp_request_join(const struct net_device *dev, 345 const struct garp_application *appl, 346 const void *data, u8 len, u8 type) 347{ 348 struct garp_port *port = rtnl_dereference(dev->garp_port); 349 struct garp_applicant *app = rtnl_dereference(port->applicants[appl->type]); 350 struct garp_attr *attr; 351 352 spin_lock_bh(&app->lock); 353 attr = garp_attr_create(app, data, len, type); 354 if (!attr) { 355 spin_unlock_bh(&app->lock); 356 return -ENOMEM; 357 } 358 garp_attr_event(app, attr, GARP_EVENT_REQ_JOIN); 359 spin_unlock_bh(&app->lock); 360 return 0; 361} 362EXPORT_SYMBOL_GPL(garp_request_join); 363 364void garp_request_leave(const struct net_device *dev, 365 const struct garp_application *appl, 366 const void *data, u8 len, u8 type) 367{ 368 struct garp_port *port = rtnl_dereference(dev->garp_port); 369 struct garp_applicant *app = rtnl_dereference(port->applicants[appl->type]); 370 struct garp_attr *attr; 371 372 spin_lock_bh(&app->lock); 373 attr = garp_attr_lookup(app, data, len, type); 374 if (!attr) { 375 spin_unlock_bh(&app->lock); 376 return; 377 } 378 garp_attr_event(app, attr, GARP_EVENT_REQ_LEAVE); 379 spin_unlock_bh(&app->lock); 380} 381EXPORT_SYMBOL_GPL(garp_request_leave); 382 383static void garp_gid_event(struct garp_applicant *app, enum garp_event event) 384{ 385 struct rb_node *node, *next; 386 struct garp_attr *attr; 387 388 for (node = rb_first(&app->gid); 389 next = node ? rb_next(node) : NULL, node != NULL; 390 node = next) { 391 attr = rb_entry(node, struct garp_attr, node); 392 garp_attr_event(app, attr, event); 393 } 394} 395 396static void garp_join_timer_arm(struct garp_applicant *app) 397{ 398 unsigned long delay; 399 400 delay = (u64)msecs_to_jiffies(garp_join_time) * prandom_u32() >> 32; 401 mod_timer(&app->join_timer, jiffies + delay); 402} 403 404static void garp_join_timer(unsigned long data) 405{ 406 struct garp_applicant *app = (struct garp_applicant *)data; 407 408 spin_lock(&app->lock); 409 garp_gid_event(app, GARP_EVENT_TRANSMIT_PDU); 410 garp_pdu_queue(app); 411 spin_unlock(&app->lock); 412 413 garp_queue_xmit(app); 414 garp_join_timer_arm(app); 415} 416 417static int garp_pdu_parse_end_mark(struct sk_buff *skb) 418{ 419 if (!pskb_may_pull(skb, sizeof(u8))) 420 return -1; 421 if (*skb->data == GARP_END_MARK) { 422 skb_pull(skb, sizeof(u8)); 423 return -1; 424 } 425 return 0; 426} 427 428static int garp_pdu_parse_attr(struct garp_applicant *app, struct sk_buff *skb, 429 u8 attrtype) 430{ 431 const struct garp_attr_hdr *ga; 432 struct garp_attr *attr; 433 enum garp_event event; 434 unsigned int dlen; 435 436 if (!pskb_may_pull(skb, sizeof(*ga))) 437 return -1; 438 ga = (struct garp_attr_hdr *)skb->data; 439 if (ga->len < sizeof(*ga)) 440 return -1; 441 442 if (!pskb_may_pull(skb, ga->len)) 443 return -1; 444 skb_pull(skb, ga->len); 445 dlen = sizeof(*ga) - ga->len; 446 447 if (attrtype > app->app->maxattr) 448 return 0; 449 450 switch (ga->event) { 451 case GARP_LEAVE_ALL: 452 if (dlen != 0) 453 return -1; 454 garp_gid_event(app, GARP_EVENT_R_LEAVE_EMPTY); 455 return 0; 456 case GARP_JOIN_EMPTY: 457 event = GARP_EVENT_R_JOIN_EMPTY; 458 break; 459 case GARP_JOIN_IN: 460 event = GARP_EVENT_R_JOIN_IN; 461 break; 462 case GARP_LEAVE_EMPTY: 463 event = GARP_EVENT_R_LEAVE_EMPTY; 464 break; 465 case GARP_EMPTY: 466 event = GARP_EVENT_R_EMPTY; 467 break; 468 default: 469 return 0; 470 } 471 472 if (dlen == 0) 473 return -1; 474 attr = garp_attr_lookup(app, ga->data, dlen, attrtype); 475 if (attr == NULL) 476 return 0; 477 garp_attr_event(app, attr, event); 478 return 0; 479} 480 481static int garp_pdu_parse_msg(struct garp_applicant *app, struct sk_buff *skb) 482{ 483 const struct garp_msg_hdr *gm; 484 485 if (!pskb_may_pull(skb, sizeof(*gm))) 486 return -1; 487 gm = (struct garp_msg_hdr *)skb->data; 488 if (gm->attrtype == 0) 489 return -1; 490 skb_pull(skb, sizeof(*gm)); 491 492 while (skb->len > 0) { 493 if (garp_pdu_parse_attr(app, skb, gm->attrtype) < 0) 494 return -1; 495 if (garp_pdu_parse_end_mark(skb) < 0) 496 break; 497 } 498 return 0; 499} 500 501static void garp_pdu_rcv(const struct stp_proto *proto, struct sk_buff *skb, 502 struct net_device *dev) 503{ 504 struct garp_application *appl = proto->data; 505 struct garp_port *port; 506 struct garp_applicant *app; 507 const struct garp_pdu_hdr *gp; 508 509 port = rcu_dereference(dev->garp_port); 510 if (!port) 511 goto err; 512 app = rcu_dereference(port->applicants[appl->type]); 513 if (!app) 514 goto err; 515 516 if (!pskb_may_pull(skb, sizeof(*gp))) 517 goto err; 518 gp = (struct garp_pdu_hdr *)skb->data; 519 if (get_unaligned(&gp->protocol) != htons(GARP_PROTOCOL_ID)) 520 goto err; 521 skb_pull(skb, sizeof(*gp)); 522 523 spin_lock(&app->lock); 524 while (skb->len > 0) { 525 if (garp_pdu_parse_msg(app, skb) < 0) 526 break; 527 if (garp_pdu_parse_end_mark(skb) < 0) 528 break; 529 } 530 spin_unlock(&app->lock); 531err: 532 kfree_skb(skb); 533} 534 535static int garp_init_port(struct net_device *dev) 536{ 537 struct garp_port *port; 538 539 port = kzalloc(sizeof(*port), GFP_KERNEL); 540 if (!port) 541 return -ENOMEM; 542 rcu_assign_pointer(dev->garp_port, port); 543 return 0; 544} 545 546static void garp_release_port(struct net_device *dev) 547{ 548 struct garp_port *port = rtnl_dereference(dev->garp_port); 549 unsigned int i; 550 551 for (i = 0; i <= GARP_APPLICATION_MAX; i++) { 552 if (rtnl_dereference(port->applicants[i])) 553 return; 554 } 555 RCU_INIT_POINTER(dev->garp_port, NULL); 556 kfree_rcu(port, rcu); 557} 558 559int garp_init_applicant(struct net_device *dev, struct garp_application *appl) 560{ 561 struct garp_applicant *app; 562 int err; 563 564 ASSERT_RTNL(); 565 566 if (!rtnl_dereference(dev->garp_port)) { 567 err = garp_init_port(dev); 568 if (err < 0) 569 goto err1; 570 } 571 572 err = -ENOMEM; 573 app = kzalloc(sizeof(*app), GFP_KERNEL); 574 if (!app) 575 goto err2; 576 577 err = dev_mc_add(dev, appl->proto.group_address); 578 if (err < 0) 579 goto err3; 580 581 app->dev = dev; 582 app->app = appl; 583 app->gid = RB_ROOT; 584 spin_lock_init(&app->lock); 585 skb_queue_head_init(&app->queue); 586 rcu_assign_pointer(dev->garp_port->applicants[appl->type], app); 587 setup_timer(&app->join_timer, garp_join_timer, (unsigned long)app); 588 garp_join_timer_arm(app); 589 return 0; 590 591err3: 592 kfree(app); 593err2: 594 garp_release_port(dev); 595err1: 596 return err; 597} 598EXPORT_SYMBOL_GPL(garp_init_applicant); 599 600void garp_uninit_applicant(struct net_device *dev, struct garp_application *appl) 601{ 602 struct garp_port *port = rtnl_dereference(dev->garp_port); 603 struct garp_applicant *app = rtnl_dereference(port->applicants[appl->type]); 604 605 ASSERT_RTNL(); 606 607 RCU_INIT_POINTER(port->applicants[appl->type], NULL); 608 609 /* Delete timer and generate a final TRANSMIT_PDU event to flush out 610 * all pending messages before the applicant is gone. */ 611 del_timer_sync(&app->join_timer); 612 613 spin_lock_bh(&app->lock); 614 garp_gid_event(app, GARP_EVENT_TRANSMIT_PDU); 615 garp_pdu_queue(app); 616 spin_unlock_bh(&app->lock); 617 618 garp_queue_xmit(app); 619 620 dev_mc_del(dev, appl->proto.group_address); 621 kfree_rcu(app, rcu); 622 garp_release_port(dev); 623} 624EXPORT_SYMBOL_GPL(garp_uninit_applicant); 625 626int garp_register_application(struct garp_application *appl) 627{ 628 appl->proto.rcv = garp_pdu_rcv; 629 appl->proto.data = appl; 630 return stp_proto_register(&appl->proto); 631} 632EXPORT_SYMBOL_GPL(garp_register_application); 633 634void garp_unregister_application(struct garp_application *appl) 635{ 636 stp_proto_unregister(&appl->proto); 637} 638EXPORT_SYMBOL_GPL(garp_unregister_application); 639