123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639 |
- /*
- * IEEE 802.1D Generic Attribute Registration Protocol (GARP)
- *
- * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- */
- #include <linux/kernel.h>
- #include <linux/timer.h>
- #include <linux/skbuff.h>
- #include <linux/netdevice.h>
- #include <linux/etherdevice.h>
- #include <linux/rtnetlink.h>
- #include <linux/llc.h>
- #include <linux/slab.h>
- #include <linux/module.h>
- #include <net/llc.h>
- #include <net/llc_pdu.h>
- #include <net/garp.h>
- #include <asm/unaligned.h>
- static unsigned int garp_join_time __read_mostly = 200;
- module_param(garp_join_time, uint, 0644);
- MODULE_PARM_DESC(garp_join_time, "Join time in ms (default 200ms)");
- MODULE_LICENSE("GPL");
- static const struct garp_state_trans {
- u8 state;
- u8 action;
- } garp_applicant_state_table[GARP_APPLICANT_MAX + 1][GARP_EVENT_MAX + 1] = {
- [GARP_APPLICANT_VA] = {
- [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_AA,
- .action = GARP_ACTION_S_JOIN_IN },
- [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_AA },
- [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VA },
- [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VA },
- [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VA },
- [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP },
- [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID },
- [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_LA },
- },
- [GARP_APPLICANT_AA] = {
- [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_QA,
- .action = GARP_ACTION_S_JOIN_IN },
- [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QA },
- [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VA },
- [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VA },
- [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VA },
- [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP },
- [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID },
- [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_LA },
- },
- [GARP_APPLICANT_QA] = {
- [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID },
- [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QA },
- [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VA },
- [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VA },
- [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP },
- [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP },
- [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID },
- [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_LA },
- },
- [GARP_APPLICANT_LA] = {
- [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_VO,
- .action = GARP_ACTION_S_LEAVE_EMPTY },
- [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_LA },
- [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO },
- [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_LA },
- [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_LA },
- [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO },
- [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_VA },
- [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID },
- },
- [GARP_APPLICANT_VP] = {
- [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_AA,
- .action = GARP_ACTION_S_JOIN_IN },
- [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_AP },
- [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VP },
- [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VP },
- [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP },
- [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP },
- [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID },
- [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_VO },
- },
- [GARP_APPLICANT_AP] = {
- [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_QA,
- .action = GARP_ACTION_S_JOIN_IN },
- [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QP },
- [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VP },
- [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VP },
- [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP },
- [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP },
- [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID },
- [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_AO },
- },
- [GARP_APPLICANT_QP] = {
- [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID },
- [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QP },
- [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VP },
- [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VP },
- [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP },
- [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP },
- [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID },
- [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_QO },
- },
- [GARP_APPLICANT_VO] = {
- [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID },
- [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_AO },
- [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO },
- [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VO },
- [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VO },
- [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO },
- [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_VP },
- [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID },
- },
- [GARP_APPLICANT_AO] = {
- [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID },
- [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QO },
- [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO },
- [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VO },
- [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VO },
- [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO },
- [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_AP },
- [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID },
- },
- [GARP_APPLICANT_QO] = {
- [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID },
- [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QO },
- [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO },
- [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VO },
- [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VO },
- [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO },
- [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_QP },
- [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID },
- },
- };
- static int garp_attr_cmp(const struct garp_attr *attr,
- const void *data, u8 len, u8 type)
- {
- if (attr->type != type)
- return attr->type - type;
- if (attr->dlen != len)
- return attr->dlen - len;
- return memcmp(attr->data, data, len);
- }
- static struct garp_attr *garp_attr_lookup(const struct garp_applicant *app,
- const void *data, u8 len, u8 type)
- {
- struct rb_node *parent = app->gid.rb_node;
- struct garp_attr *attr;
- int d;
- while (parent) {
- attr = rb_entry(parent, struct garp_attr, node);
- d = garp_attr_cmp(attr, data, len, type);
- if (d > 0)
- parent = parent->rb_left;
- else if (d < 0)
- parent = parent->rb_right;
- else
- return attr;
- }
- return NULL;
- }
- static struct garp_attr *garp_attr_create(struct garp_applicant *app,
- const void *data, u8 len, u8 type)
- {
- struct rb_node *parent = NULL, **p = &app->gid.rb_node;
- struct garp_attr *attr;
- int d;
- while (*p) {
- parent = *p;
- attr = rb_entry(parent, struct garp_attr, node);
- d = garp_attr_cmp(attr, data, len, type);
- if (d > 0)
- p = &parent->rb_left;
- else if (d < 0)
- p = &parent->rb_right;
- else {
- /* The attribute already exists; re-use it. */
- return attr;
- }
- }
- attr = kmalloc(sizeof(*attr) + len, GFP_ATOMIC);
- if (!attr)
- return attr;
- attr->state = GARP_APPLICANT_VO;
- attr->type = type;
- attr->dlen = len;
- memcpy(attr->data, data, len);
- rb_link_node(&attr->node, parent, p);
- rb_insert_color(&attr->node, &app->gid);
- return attr;
- }
- static void garp_attr_destroy(struct garp_applicant *app, struct garp_attr *attr)
- {
- rb_erase(&attr->node, &app->gid);
- kfree(attr);
- }
- static int garp_pdu_init(struct garp_applicant *app)
- {
- struct sk_buff *skb;
- struct garp_pdu_hdr *gp;
- #define LLC_RESERVE sizeof(struct llc_pdu_un)
- skb = alloc_skb(app->dev->mtu + LL_RESERVED_SPACE(app->dev),
- GFP_ATOMIC);
- if (!skb)
- return -ENOMEM;
- skb->dev = app->dev;
- skb->protocol = htons(ETH_P_802_2);
- skb_reserve(skb, LL_RESERVED_SPACE(app->dev) + LLC_RESERVE);
- gp = __skb_put(skb, sizeof(*gp));
- put_unaligned(htons(GARP_PROTOCOL_ID), &gp->protocol);
- app->pdu = skb;
- return 0;
- }
- static int garp_pdu_append_end_mark(struct garp_applicant *app)
- {
- if (skb_tailroom(app->pdu) < sizeof(u8))
- return -1;
- __skb_put_u8(app->pdu, GARP_END_MARK);
- return 0;
- }
- static void garp_pdu_queue(struct garp_applicant *app)
- {
- if (!app->pdu)
- return;
- garp_pdu_append_end_mark(app);
- garp_pdu_append_end_mark(app);
- llc_pdu_header_init(app->pdu, LLC_PDU_TYPE_U, LLC_SAP_BSPAN,
- LLC_SAP_BSPAN, LLC_PDU_CMD);
- llc_pdu_init_as_ui_cmd(app->pdu);
- llc_mac_hdr_init(app->pdu, app->dev->dev_addr,
- app->app->proto.group_address);
- skb_queue_tail(&app->queue, app->pdu);
- app->pdu = NULL;
- }
- static void garp_queue_xmit(struct garp_applicant *app)
- {
- struct sk_buff *skb;
- while ((skb = skb_dequeue(&app->queue)))
- dev_queue_xmit(skb);
- }
- static int garp_pdu_append_msg(struct garp_applicant *app, u8 attrtype)
- {
- struct garp_msg_hdr *gm;
- if (skb_tailroom(app->pdu) < sizeof(*gm))
- return -1;
- gm = __skb_put(app->pdu, sizeof(*gm));
- gm->attrtype = attrtype;
- garp_cb(app->pdu)->cur_type = attrtype;
- return 0;
- }
- static int garp_pdu_append_attr(struct garp_applicant *app,
- const struct garp_attr *attr,
- enum garp_attr_event event)
- {
- struct garp_attr_hdr *ga;
- unsigned int len;
- int err;
- again:
- if (!app->pdu) {
- err = garp_pdu_init(app);
- if (err < 0)
- return err;
- }
- if (garp_cb(app->pdu)->cur_type != attr->type) {
- if (garp_cb(app->pdu)->cur_type &&
- garp_pdu_append_end_mark(app) < 0)
- goto queue;
- if (garp_pdu_append_msg(app, attr->type) < 0)
- goto queue;
- }
- len = sizeof(*ga) + attr->dlen;
- if (skb_tailroom(app->pdu) < len)
- goto queue;
- ga = __skb_put(app->pdu, len);
- ga->len = len;
- ga->event = event;
- memcpy(ga->data, attr->data, attr->dlen);
- return 0;
- queue:
- garp_pdu_queue(app);
- goto again;
- }
- static void garp_attr_event(struct garp_applicant *app,
- struct garp_attr *attr, enum garp_event event)
- {
- enum garp_applicant_state state;
- state = garp_applicant_state_table[attr->state][event].state;
- if (state == GARP_APPLICANT_INVALID)
- return;
- switch (garp_applicant_state_table[attr->state][event].action) {
- case GARP_ACTION_NONE:
- break;
- case GARP_ACTION_S_JOIN_IN:
- /* When appending the attribute fails, don't update state in
- * order to retry on next TRANSMIT_PDU event. */
- if (garp_pdu_append_attr(app, attr, GARP_JOIN_IN) < 0)
- return;
- break;
- case GARP_ACTION_S_LEAVE_EMPTY:
- garp_pdu_append_attr(app, attr, GARP_LEAVE_EMPTY);
- /* As a pure applicant, sending a leave message implies that
- * the attribute was unregistered and can be destroyed. */
- garp_attr_destroy(app, attr);
- return;
- default:
- WARN_ON(1);
- }
- attr->state = state;
- }
- int garp_request_join(const struct net_device *dev,
- const struct garp_application *appl,
- const void *data, u8 len, u8 type)
- {
- struct garp_port *port = rtnl_dereference(dev->garp_port);
- struct garp_applicant *app = rtnl_dereference(port->applicants[appl->type]);
- struct garp_attr *attr;
- spin_lock_bh(&app->lock);
- attr = garp_attr_create(app, data, len, type);
- if (!attr) {
- spin_unlock_bh(&app->lock);
- return -ENOMEM;
- }
- garp_attr_event(app, attr, GARP_EVENT_REQ_JOIN);
- spin_unlock_bh(&app->lock);
- return 0;
- }
- EXPORT_SYMBOL_GPL(garp_request_join);
- void garp_request_leave(const struct net_device *dev,
- const struct garp_application *appl,
- const void *data, u8 len, u8 type)
- {
- struct garp_port *port = rtnl_dereference(dev->garp_port);
- struct garp_applicant *app = rtnl_dereference(port->applicants[appl->type]);
- struct garp_attr *attr;
- spin_lock_bh(&app->lock);
- attr = garp_attr_lookup(app, data, len, type);
- if (!attr) {
- spin_unlock_bh(&app->lock);
- return;
- }
- garp_attr_event(app, attr, GARP_EVENT_REQ_LEAVE);
- spin_unlock_bh(&app->lock);
- }
- EXPORT_SYMBOL_GPL(garp_request_leave);
- static void garp_gid_event(struct garp_applicant *app, enum garp_event event)
- {
- struct rb_node *node, *next;
- struct garp_attr *attr;
- for (node = rb_first(&app->gid);
- next = node ? rb_next(node) : NULL, node != NULL;
- node = next) {
- attr = rb_entry(node, struct garp_attr, node);
- garp_attr_event(app, attr, event);
- }
- }
- static void garp_join_timer_arm(struct garp_applicant *app)
- {
- unsigned long delay;
- delay = (u64)msecs_to_jiffies(garp_join_time) * prandom_u32() >> 32;
- mod_timer(&app->join_timer, jiffies + delay);
- }
- static void garp_join_timer(struct timer_list *t)
- {
- struct garp_applicant *app = from_timer(app, t, join_timer);
- spin_lock(&app->lock);
- garp_gid_event(app, GARP_EVENT_TRANSMIT_PDU);
- garp_pdu_queue(app);
- spin_unlock(&app->lock);
- garp_queue_xmit(app);
- garp_join_timer_arm(app);
- }
- static int garp_pdu_parse_end_mark(struct sk_buff *skb)
- {
- if (!pskb_may_pull(skb, sizeof(u8)))
- return -1;
- if (*skb->data == GARP_END_MARK) {
- skb_pull(skb, sizeof(u8));
- return -1;
- }
- return 0;
- }
- static int garp_pdu_parse_attr(struct garp_applicant *app, struct sk_buff *skb,
- u8 attrtype)
- {
- const struct garp_attr_hdr *ga;
- struct garp_attr *attr;
- enum garp_event event;
- unsigned int dlen;
- if (!pskb_may_pull(skb, sizeof(*ga)))
- return -1;
- ga = (struct garp_attr_hdr *)skb->data;
- if (ga->len < sizeof(*ga))
- return -1;
- if (!pskb_may_pull(skb, ga->len))
- return -1;
- skb_pull(skb, ga->len);
- dlen = sizeof(*ga) - ga->len;
- if (attrtype > app->app->maxattr)
- return 0;
- switch (ga->event) {
- case GARP_LEAVE_ALL:
- if (dlen != 0)
- return -1;
- garp_gid_event(app, GARP_EVENT_R_LEAVE_EMPTY);
- return 0;
- case GARP_JOIN_EMPTY:
- event = GARP_EVENT_R_JOIN_EMPTY;
- break;
- case GARP_JOIN_IN:
- event = GARP_EVENT_R_JOIN_IN;
- break;
- case GARP_LEAVE_EMPTY:
- event = GARP_EVENT_R_LEAVE_EMPTY;
- break;
- case GARP_EMPTY:
- event = GARP_EVENT_R_EMPTY;
- break;
- default:
- return 0;
- }
- if (dlen == 0)
- return -1;
- attr = garp_attr_lookup(app, ga->data, dlen, attrtype);
- if (attr == NULL)
- return 0;
- garp_attr_event(app, attr, event);
- return 0;
- }
- static int garp_pdu_parse_msg(struct garp_applicant *app, struct sk_buff *skb)
- {
- const struct garp_msg_hdr *gm;
- if (!pskb_may_pull(skb, sizeof(*gm)))
- return -1;
- gm = (struct garp_msg_hdr *)skb->data;
- if (gm->attrtype == 0)
- return -1;
- skb_pull(skb, sizeof(*gm));
- while (skb->len > 0) {
- if (garp_pdu_parse_attr(app, skb, gm->attrtype) < 0)
- return -1;
- if (garp_pdu_parse_end_mark(skb) < 0)
- break;
- }
- return 0;
- }
- static void garp_pdu_rcv(const struct stp_proto *proto, struct sk_buff *skb,
- struct net_device *dev)
- {
- struct garp_application *appl = proto->data;
- struct garp_port *port;
- struct garp_applicant *app;
- const struct garp_pdu_hdr *gp;
- port = rcu_dereference(dev->garp_port);
- if (!port)
- goto err;
- app = rcu_dereference(port->applicants[appl->type]);
- if (!app)
- goto err;
- if (!pskb_may_pull(skb, sizeof(*gp)))
- goto err;
- gp = (struct garp_pdu_hdr *)skb->data;
- if (get_unaligned(&gp->protocol) != htons(GARP_PROTOCOL_ID))
- goto err;
- skb_pull(skb, sizeof(*gp));
- spin_lock(&app->lock);
- while (skb->len > 0) {
- if (garp_pdu_parse_msg(app, skb) < 0)
- break;
- if (garp_pdu_parse_end_mark(skb) < 0)
- break;
- }
- spin_unlock(&app->lock);
- err:
- kfree_skb(skb);
- }
- static int garp_init_port(struct net_device *dev)
- {
- struct garp_port *port;
- port = kzalloc(sizeof(*port), GFP_KERNEL);
- if (!port)
- return -ENOMEM;
- rcu_assign_pointer(dev->garp_port, port);
- return 0;
- }
- static void garp_release_port(struct net_device *dev)
- {
- struct garp_port *port = rtnl_dereference(dev->garp_port);
- unsigned int i;
- for (i = 0; i <= GARP_APPLICATION_MAX; i++) {
- if (rtnl_dereference(port->applicants[i]))
- return;
- }
- RCU_INIT_POINTER(dev->garp_port, NULL);
- kfree_rcu(port, rcu);
- }
- int garp_init_applicant(struct net_device *dev, struct garp_application *appl)
- {
- struct garp_applicant *app;
- int err;
- ASSERT_RTNL();
- if (!rtnl_dereference(dev->garp_port)) {
- err = garp_init_port(dev);
- if (err < 0)
- goto err1;
- }
- err = -ENOMEM;
- app = kzalloc(sizeof(*app), GFP_KERNEL);
- if (!app)
- goto err2;
- err = dev_mc_add(dev, appl->proto.group_address);
- if (err < 0)
- goto err3;
- app->dev = dev;
- app->app = appl;
- app->gid = RB_ROOT;
- spin_lock_init(&app->lock);
- skb_queue_head_init(&app->queue);
- rcu_assign_pointer(dev->garp_port->applicants[appl->type], app);
- timer_setup(&app->join_timer, garp_join_timer, 0);
- garp_join_timer_arm(app);
- return 0;
- err3:
- kfree(app);
- err2:
- garp_release_port(dev);
- err1:
- return err;
- }
- EXPORT_SYMBOL_GPL(garp_init_applicant);
- void garp_uninit_applicant(struct net_device *dev, struct garp_application *appl)
- {
- struct garp_port *port = rtnl_dereference(dev->garp_port);
- struct garp_applicant *app = rtnl_dereference(port->applicants[appl->type]);
- ASSERT_RTNL();
- RCU_INIT_POINTER(port->applicants[appl->type], NULL);
- /* Delete timer and generate a final TRANSMIT_PDU event to flush out
- * all pending messages before the applicant is gone. */
- del_timer_sync(&app->join_timer);
- spin_lock_bh(&app->lock);
- garp_gid_event(app, GARP_EVENT_TRANSMIT_PDU);
- garp_pdu_queue(app);
- spin_unlock_bh(&app->lock);
- garp_queue_xmit(app);
- dev_mc_del(dev, appl->proto.group_address);
- kfree_rcu(app, rcu);
- garp_release_port(dev);
- }
- EXPORT_SYMBOL_GPL(garp_uninit_applicant);
- int garp_register_application(struct garp_application *appl)
- {
- appl->proto.rcv = garp_pdu_rcv;
- appl->proto.data = appl;
- return stp_proto_register(&appl->proto);
- }
- EXPORT_SYMBOL_GPL(garp_register_application);
- void garp_unregister_application(struct garp_application *appl)
- {
- stp_proto_unregister(&appl->proto);
- }
- EXPORT_SYMBOL_GPL(garp_unregister_application);
|