18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *	IEEE 802.1D Generic Attribute Registration Protocol (GARP)
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *	Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/kernel.h>
88c2ecf20Sopenharmony_ci#include <linux/timer.h>
98c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
108c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
118c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
128c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
138c2ecf20Sopenharmony_ci#include <linux/llc.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <net/llc.h>
178c2ecf20Sopenharmony_ci#include <net/llc_pdu.h>
188c2ecf20Sopenharmony_ci#include <net/garp.h>
198c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic unsigned int garp_join_time __read_mostly = 200;
228c2ecf20Sopenharmony_cimodule_param(garp_join_time, uint, 0644);
238c2ecf20Sopenharmony_ciMODULE_PARM_DESC(garp_join_time, "Join time in ms (default 200ms)");
248c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic const struct garp_state_trans {
278c2ecf20Sopenharmony_ci	u8	state;
288c2ecf20Sopenharmony_ci	u8	action;
298c2ecf20Sopenharmony_ci} garp_applicant_state_table[GARP_APPLICANT_MAX + 1][GARP_EVENT_MAX + 1] = {
308c2ecf20Sopenharmony_ci	[GARP_APPLICANT_VA] = {
318c2ecf20Sopenharmony_ci		[GARP_EVENT_TRANSMIT_PDU]	= { .state = GARP_APPLICANT_AA,
328c2ecf20Sopenharmony_ci						    .action = GARP_ACTION_S_JOIN_IN },
338c2ecf20Sopenharmony_ci		[GARP_EVENT_R_JOIN_IN]		= { .state = GARP_APPLICANT_AA },
348c2ecf20Sopenharmony_ci		[GARP_EVENT_R_JOIN_EMPTY]	= { .state = GARP_APPLICANT_VA },
358c2ecf20Sopenharmony_ci		[GARP_EVENT_R_EMPTY]		= { .state = GARP_APPLICANT_VA },
368c2ecf20Sopenharmony_ci		[GARP_EVENT_R_LEAVE_IN]		= { .state = GARP_APPLICANT_VA },
378c2ecf20Sopenharmony_ci		[GARP_EVENT_R_LEAVE_EMPTY]	= { .state = GARP_APPLICANT_VP },
388c2ecf20Sopenharmony_ci		[GARP_EVENT_REQ_JOIN]		= { .state = GARP_APPLICANT_INVALID },
398c2ecf20Sopenharmony_ci		[GARP_EVENT_REQ_LEAVE]		= { .state = GARP_APPLICANT_LA },
408c2ecf20Sopenharmony_ci	},
418c2ecf20Sopenharmony_ci	[GARP_APPLICANT_AA] = {
428c2ecf20Sopenharmony_ci		[GARP_EVENT_TRANSMIT_PDU]	= { .state = GARP_APPLICANT_QA,
438c2ecf20Sopenharmony_ci						    .action = GARP_ACTION_S_JOIN_IN },
448c2ecf20Sopenharmony_ci		[GARP_EVENT_R_JOIN_IN]		= { .state = GARP_APPLICANT_QA },
458c2ecf20Sopenharmony_ci		[GARP_EVENT_R_JOIN_EMPTY]	= { .state = GARP_APPLICANT_VA },
468c2ecf20Sopenharmony_ci		[GARP_EVENT_R_EMPTY]		= { .state = GARP_APPLICANT_VA },
478c2ecf20Sopenharmony_ci		[GARP_EVENT_R_LEAVE_IN]		= { .state = GARP_APPLICANT_VA },
488c2ecf20Sopenharmony_ci		[GARP_EVENT_R_LEAVE_EMPTY]	= { .state = GARP_APPLICANT_VP },
498c2ecf20Sopenharmony_ci		[GARP_EVENT_REQ_JOIN]		= { .state = GARP_APPLICANT_INVALID },
508c2ecf20Sopenharmony_ci		[GARP_EVENT_REQ_LEAVE]		= { .state = GARP_APPLICANT_LA },
518c2ecf20Sopenharmony_ci	},
528c2ecf20Sopenharmony_ci	[GARP_APPLICANT_QA] = {
538c2ecf20Sopenharmony_ci		[GARP_EVENT_TRANSMIT_PDU]	= { .state = GARP_APPLICANT_INVALID },
548c2ecf20Sopenharmony_ci		[GARP_EVENT_R_JOIN_IN]		= { .state = GARP_APPLICANT_QA },
558c2ecf20Sopenharmony_ci		[GARP_EVENT_R_JOIN_EMPTY]	= { .state = GARP_APPLICANT_VA },
568c2ecf20Sopenharmony_ci		[GARP_EVENT_R_EMPTY]		= { .state = GARP_APPLICANT_VA },
578c2ecf20Sopenharmony_ci		[GARP_EVENT_R_LEAVE_IN]		= { .state = GARP_APPLICANT_VP },
588c2ecf20Sopenharmony_ci		[GARP_EVENT_R_LEAVE_EMPTY]	= { .state = GARP_APPLICANT_VP },
598c2ecf20Sopenharmony_ci		[GARP_EVENT_REQ_JOIN]		= { .state = GARP_APPLICANT_INVALID },
608c2ecf20Sopenharmony_ci		[GARP_EVENT_REQ_LEAVE]		= { .state = GARP_APPLICANT_LA },
618c2ecf20Sopenharmony_ci	},
628c2ecf20Sopenharmony_ci	[GARP_APPLICANT_LA] = {
638c2ecf20Sopenharmony_ci		[GARP_EVENT_TRANSMIT_PDU]	= { .state = GARP_APPLICANT_VO,
648c2ecf20Sopenharmony_ci						    .action = GARP_ACTION_S_LEAVE_EMPTY },
658c2ecf20Sopenharmony_ci		[GARP_EVENT_R_JOIN_IN]		= { .state = GARP_APPLICANT_LA },
668c2ecf20Sopenharmony_ci		[GARP_EVENT_R_JOIN_EMPTY]	= { .state = GARP_APPLICANT_VO },
678c2ecf20Sopenharmony_ci		[GARP_EVENT_R_EMPTY]		= { .state = GARP_APPLICANT_LA },
688c2ecf20Sopenharmony_ci		[GARP_EVENT_R_LEAVE_IN]		= { .state = GARP_APPLICANT_LA },
698c2ecf20Sopenharmony_ci		[GARP_EVENT_R_LEAVE_EMPTY]	= { .state = GARP_APPLICANT_VO },
708c2ecf20Sopenharmony_ci		[GARP_EVENT_REQ_JOIN]		= { .state = GARP_APPLICANT_VA },
718c2ecf20Sopenharmony_ci		[GARP_EVENT_REQ_LEAVE]		= { .state = GARP_APPLICANT_INVALID },
728c2ecf20Sopenharmony_ci	},
738c2ecf20Sopenharmony_ci	[GARP_APPLICANT_VP] = {
748c2ecf20Sopenharmony_ci		[GARP_EVENT_TRANSMIT_PDU]	= { .state = GARP_APPLICANT_AA,
758c2ecf20Sopenharmony_ci						    .action = GARP_ACTION_S_JOIN_IN },
768c2ecf20Sopenharmony_ci		[GARP_EVENT_R_JOIN_IN]		= { .state = GARP_APPLICANT_AP },
778c2ecf20Sopenharmony_ci		[GARP_EVENT_R_JOIN_EMPTY]	= { .state = GARP_APPLICANT_VP },
788c2ecf20Sopenharmony_ci		[GARP_EVENT_R_EMPTY]		= { .state = GARP_APPLICANT_VP },
798c2ecf20Sopenharmony_ci		[GARP_EVENT_R_LEAVE_IN]		= { .state = GARP_APPLICANT_VP },
808c2ecf20Sopenharmony_ci		[GARP_EVENT_R_LEAVE_EMPTY]	= { .state = GARP_APPLICANT_VP },
818c2ecf20Sopenharmony_ci		[GARP_EVENT_REQ_JOIN]		= { .state = GARP_APPLICANT_INVALID },
828c2ecf20Sopenharmony_ci		[GARP_EVENT_REQ_LEAVE]		= { .state = GARP_APPLICANT_VO },
838c2ecf20Sopenharmony_ci	},
848c2ecf20Sopenharmony_ci	[GARP_APPLICANT_AP] = {
858c2ecf20Sopenharmony_ci		[GARP_EVENT_TRANSMIT_PDU]	= { .state = GARP_APPLICANT_QA,
868c2ecf20Sopenharmony_ci						    .action = GARP_ACTION_S_JOIN_IN },
878c2ecf20Sopenharmony_ci		[GARP_EVENT_R_JOIN_IN]		= { .state = GARP_APPLICANT_QP },
888c2ecf20Sopenharmony_ci		[GARP_EVENT_R_JOIN_EMPTY]	= { .state = GARP_APPLICANT_VP },
898c2ecf20Sopenharmony_ci		[GARP_EVENT_R_EMPTY]		= { .state = GARP_APPLICANT_VP },
908c2ecf20Sopenharmony_ci		[GARP_EVENT_R_LEAVE_IN]		= { .state = GARP_APPLICANT_VP },
918c2ecf20Sopenharmony_ci		[GARP_EVENT_R_LEAVE_EMPTY]	= { .state = GARP_APPLICANT_VP },
928c2ecf20Sopenharmony_ci		[GARP_EVENT_REQ_JOIN]		= { .state = GARP_APPLICANT_INVALID },
938c2ecf20Sopenharmony_ci		[GARP_EVENT_REQ_LEAVE]		= { .state = GARP_APPLICANT_AO },
948c2ecf20Sopenharmony_ci	},
958c2ecf20Sopenharmony_ci	[GARP_APPLICANT_QP] = {
968c2ecf20Sopenharmony_ci		[GARP_EVENT_TRANSMIT_PDU]	= { .state = GARP_APPLICANT_INVALID },
978c2ecf20Sopenharmony_ci		[GARP_EVENT_R_JOIN_IN]		= { .state = GARP_APPLICANT_QP },
988c2ecf20Sopenharmony_ci		[GARP_EVENT_R_JOIN_EMPTY]	= { .state = GARP_APPLICANT_VP },
998c2ecf20Sopenharmony_ci		[GARP_EVENT_R_EMPTY]		= { .state = GARP_APPLICANT_VP },
1008c2ecf20Sopenharmony_ci		[GARP_EVENT_R_LEAVE_IN]		= { .state = GARP_APPLICANT_VP },
1018c2ecf20Sopenharmony_ci		[GARP_EVENT_R_LEAVE_EMPTY]	= { .state = GARP_APPLICANT_VP },
1028c2ecf20Sopenharmony_ci		[GARP_EVENT_REQ_JOIN]		= { .state = GARP_APPLICANT_INVALID },
1038c2ecf20Sopenharmony_ci		[GARP_EVENT_REQ_LEAVE]		= { .state = GARP_APPLICANT_QO },
1048c2ecf20Sopenharmony_ci	},
1058c2ecf20Sopenharmony_ci	[GARP_APPLICANT_VO] = {
1068c2ecf20Sopenharmony_ci		[GARP_EVENT_TRANSMIT_PDU]	= { .state = GARP_APPLICANT_INVALID },
1078c2ecf20Sopenharmony_ci		[GARP_EVENT_R_JOIN_IN]		= { .state = GARP_APPLICANT_AO },
1088c2ecf20Sopenharmony_ci		[GARP_EVENT_R_JOIN_EMPTY]	= { .state = GARP_APPLICANT_VO },
1098c2ecf20Sopenharmony_ci		[GARP_EVENT_R_EMPTY]		= { .state = GARP_APPLICANT_VO },
1108c2ecf20Sopenharmony_ci		[GARP_EVENT_R_LEAVE_IN]		= { .state = GARP_APPLICANT_VO },
1118c2ecf20Sopenharmony_ci		[GARP_EVENT_R_LEAVE_EMPTY]	= { .state = GARP_APPLICANT_VO },
1128c2ecf20Sopenharmony_ci		[GARP_EVENT_REQ_JOIN]		= { .state = GARP_APPLICANT_VP },
1138c2ecf20Sopenharmony_ci		[GARP_EVENT_REQ_LEAVE]		= { .state = GARP_APPLICANT_INVALID },
1148c2ecf20Sopenharmony_ci	},
1158c2ecf20Sopenharmony_ci	[GARP_APPLICANT_AO] = {
1168c2ecf20Sopenharmony_ci		[GARP_EVENT_TRANSMIT_PDU]	= { .state = GARP_APPLICANT_INVALID },
1178c2ecf20Sopenharmony_ci		[GARP_EVENT_R_JOIN_IN]		= { .state = GARP_APPLICANT_QO },
1188c2ecf20Sopenharmony_ci		[GARP_EVENT_R_JOIN_EMPTY]	= { .state = GARP_APPLICANT_VO },
1198c2ecf20Sopenharmony_ci		[GARP_EVENT_R_EMPTY]		= { .state = GARP_APPLICANT_VO },
1208c2ecf20Sopenharmony_ci		[GARP_EVENT_R_LEAVE_IN]		= { .state = GARP_APPLICANT_VO },
1218c2ecf20Sopenharmony_ci		[GARP_EVENT_R_LEAVE_EMPTY]	= { .state = GARP_APPLICANT_VO },
1228c2ecf20Sopenharmony_ci		[GARP_EVENT_REQ_JOIN]		= { .state = GARP_APPLICANT_AP },
1238c2ecf20Sopenharmony_ci		[GARP_EVENT_REQ_LEAVE]		= { .state = GARP_APPLICANT_INVALID },
1248c2ecf20Sopenharmony_ci	},
1258c2ecf20Sopenharmony_ci	[GARP_APPLICANT_QO] = {
1268c2ecf20Sopenharmony_ci		[GARP_EVENT_TRANSMIT_PDU]	= { .state = GARP_APPLICANT_INVALID },
1278c2ecf20Sopenharmony_ci		[GARP_EVENT_R_JOIN_IN]		= { .state = GARP_APPLICANT_QO },
1288c2ecf20Sopenharmony_ci		[GARP_EVENT_R_JOIN_EMPTY]	= { .state = GARP_APPLICANT_VO },
1298c2ecf20Sopenharmony_ci		[GARP_EVENT_R_EMPTY]		= { .state = GARP_APPLICANT_VO },
1308c2ecf20Sopenharmony_ci		[GARP_EVENT_R_LEAVE_IN]		= { .state = GARP_APPLICANT_VO },
1318c2ecf20Sopenharmony_ci		[GARP_EVENT_R_LEAVE_EMPTY]	= { .state = GARP_APPLICANT_VO },
1328c2ecf20Sopenharmony_ci		[GARP_EVENT_REQ_JOIN]		= { .state = GARP_APPLICANT_QP },
1338c2ecf20Sopenharmony_ci		[GARP_EVENT_REQ_LEAVE]		= { .state = GARP_APPLICANT_INVALID },
1348c2ecf20Sopenharmony_ci	},
1358c2ecf20Sopenharmony_ci};
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic int garp_attr_cmp(const struct garp_attr *attr,
1388c2ecf20Sopenharmony_ci			 const void *data, u8 len, u8 type)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	if (attr->type != type)
1418c2ecf20Sopenharmony_ci		return attr->type - type;
1428c2ecf20Sopenharmony_ci	if (attr->dlen != len)
1438c2ecf20Sopenharmony_ci		return attr->dlen - len;
1448c2ecf20Sopenharmony_ci	return memcmp(attr->data, data, len);
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic struct garp_attr *garp_attr_lookup(const struct garp_applicant *app,
1488c2ecf20Sopenharmony_ci					  const void *data, u8 len, u8 type)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	struct rb_node *parent = app->gid.rb_node;
1518c2ecf20Sopenharmony_ci	struct garp_attr *attr;
1528c2ecf20Sopenharmony_ci	int d;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	while (parent) {
1558c2ecf20Sopenharmony_ci		attr = rb_entry(parent, struct garp_attr, node);
1568c2ecf20Sopenharmony_ci		d = garp_attr_cmp(attr, data, len, type);
1578c2ecf20Sopenharmony_ci		if (d > 0)
1588c2ecf20Sopenharmony_ci			parent = parent->rb_left;
1598c2ecf20Sopenharmony_ci		else if (d < 0)
1608c2ecf20Sopenharmony_ci			parent = parent->rb_right;
1618c2ecf20Sopenharmony_ci		else
1628c2ecf20Sopenharmony_ci			return attr;
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci	return NULL;
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic struct garp_attr *garp_attr_create(struct garp_applicant *app,
1688c2ecf20Sopenharmony_ci					  const void *data, u8 len, u8 type)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	struct rb_node *parent = NULL, **p = &app->gid.rb_node;
1718c2ecf20Sopenharmony_ci	struct garp_attr *attr;
1728c2ecf20Sopenharmony_ci	int d;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	while (*p) {
1758c2ecf20Sopenharmony_ci		parent = *p;
1768c2ecf20Sopenharmony_ci		attr = rb_entry(parent, struct garp_attr, node);
1778c2ecf20Sopenharmony_ci		d = garp_attr_cmp(attr, data, len, type);
1788c2ecf20Sopenharmony_ci		if (d > 0)
1798c2ecf20Sopenharmony_ci			p = &parent->rb_left;
1808c2ecf20Sopenharmony_ci		else if (d < 0)
1818c2ecf20Sopenharmony_ci			p = &parent->rb_right;
1828c2ecf20Sopenharmony_ci		else {
1838c2ecf20Sopenharmony_ci			/* The attribute already exists; re-use it. */
1848c2ecf20Sopenharmony_ci			return attr;
1858c2ecf20Sopenharmony_ci		}
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci	attr = kmalloc(sizeof(*attr) + len, GFP_ATOMIC);
1888c2ecf20Sopenharmony_ci	if (!attr)
1898c2ecf20Sopenharmony_ci		return attr;
1908c2ecf20Sopenharmony_ci	attr->state = GARP_APPLICANT_VO;
1918c2ecf20Sopenharmony_ci	attr->type  = type;
1928c2ecf20Sopenharmony_ci	attr->dlen  = len;
1938c2ecf20Sopenharmony_ci	memcpy(attr->data, data, len);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	rb_link_node(&attr->node, parent, p);
1968c2ecf20Sopenharmony_ci	rb_insert_color(&attr->node, &app->gid);
1978c2ecf20Sopenharmony_ci	return attr;
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic void garp_attr_destroy(struct garp_applicant *app, struct garp_attr *attr)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	rb_erase(&attr->node, &app->gid);
2038c2ecf20Sopenharmony_ci	kfree(attr);
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic void garp_attr_destroy_all(struct garp_applicant *app)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	struct rb_node *node, *next;
2098c2ecf20Sopenharmony_ci	struct garp_attr *attr;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	for (node = rb_first(&app->gid);
2128c2ecf20Sopenharmony_ci	     next = node ? rb_next(node) : NULL, node != NULL;
2138c2ecf20Sopenharmony_ci	     node = next) {
2148c2ecf20Sopenharmony_ci		attr = rb_entry(node, struct garp_attr, node);
2158c2ecf20Sopenharmony_ci		garp_attr_destroy(app, attr);
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic int garp_pdu_init(struct garp_applicant *app)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	struct sk_buff *skb;
2228c2ecf20Sopenharmony_ci	struct garp_pdu_hdr *gp;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci#define LLC_RESERVE	sizeof(struct llc_pdu_un)
2258c2ecf20Sopenharmony_ci	skb = alloc_skb(app->dev->mtu + LL_RESERVED_SPACE(app->dev),
2268c2ecf20Sopenharmony_ci			GFP_ATOMIC);
2278c2ecf20Sopenharmony_ci	if (!skb)
2288c2ecf20Sopenharmony_ci		return -ENOMEM;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	skb->dev = app->dev;
2318c2ecf20Sopenharmony_ci	skb->protocol = htons(ETH_P_802_2);
2328c2ecf20Sopenharmony_ci	skb_reserve(skb, LL_RESERVED_SPACE(app->dev) + LLC_RESERVE);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	gp = __skb_put(skb, sizeof(*gp));
2358c2ecf20Sopenharmony_ci	put_unaligned(htons(GARP_PROTOCOL_ID), &gp->protocol);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	app->pdu = skb;
2388c2ecf20Sopenharmony_ci	return 0;
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic int garp_pdu_append_end_mark(struct garp_applicant *app)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	if (skb_tailroom(app->pdu) < sizeof(u8))
2448c2ecf20Sopenharmony_ci		return -1;
2458c2ecf20Sopenharmony_ci	__skb_put_u8(app->pdu, GARP_END_MARK);
2468c2ecf20Sopenharmony_ci	return 0;
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic void garp_pdu_queue(struct garp_applicant *app)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	if (!app->pdu)
2528c2ecf20Sopenharmony_ci		return;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	garp_pdu_append_end_mark(app);
2558c2ecf20Sopenharmony_ci	garp_pdu_append_end_mark(app);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	llc_pdu_header_init(app->pdu, LLC_PDU_TYPE_U, LLC_SAP_BSPAN,
2588c2ecf20Sopenharmony_ci			    LLC_SAP_BSPAN, LLC_PDU_CMD);
2598c2ecf20Sopenharmony_ci	llc_pdu_init_as_ui_cmd(app->pdu);
2608c2ecf20Sopenharmony_ci	llc_mac_hdr_init(app->pdu, app->dev->dev_addr,
2618c2ecf20Sopenharmony_ci			 app->app->proto.group_address);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	skb_queue_tail(&app->queue, app->pdu);
2648c2ecf20Sopenharmony_ci	app->pdu = NULL;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic void garp_queue_xmit(struct garp_applicant *app)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	struct sk_buff *skb;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	while ((skb = skb_dequeue(&app->queue)))
2728c2ecf20Sopenharmony_ci		dev_queue_xmit(skb);
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_cistatic int garp_pdu_append_msg(struct garp_applicant *app, u8 attrtype)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	struct garp_msg_hdr *gm;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	if (skb_tailroom(app->pdu) < sizeof(*gm))
2808c2ecf20Sopenharmony_ci		return -1;
2818c2ecf20Sopenharmony_ci	gm = __skb_put(app->pdu, sizeof(*gm));
2828c2ecf20Sopenharmony_ci	gm->attrtype = attrtype;
2838c2ecf20Sopenharmony_ci	garp_cb(app->pdu)->cur_type = attrtype;
2848c2ecf20Sopenharmony_ci	return 0;
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_cistatic int garp_pdu_append_attr(struct garp_applicant *app,
2888c2ecf20Sopenharmony_ci				const struct garp_attr *attr,
2898c2ecf20Sopenharmony_ci				enum garp_attr_event event)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	struct garp_attr_hdr *ga;
2928c2ecf20Sopenharmony_ci	unsigned int len;
2938c2ecf20Sopenharmony_ci	int err;
2948c2ecf20Sopenharmony_ciagain:
2958c2ecf20Sopenharmony_ci	if (!app->pdu) {
2968c2ecf20Sopenharmony_ci		err = garp_pdu_init(app);
2978c2ecf20Sopenharmony_ci		if (err < 0)
2988c2ecf20Sopenharmony_ci			return err;
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	if (garp_cb(app->pdu)->cur_type != attr->type) {
3028c2ecf20Sopenharmony_ci		if (garp_cb(app->pdu)->cur_type &&
3038c2ecf20Sopenharmony_ci		    garp_pdu_append_end_mark(app) < 0)
3048c2ecf20Sopenharmony_ci			goto queue;
3058c2ecf20Sopenharmony_ci		if (garp_pdu_append_msg(app, attr->type) < 0)
3068c2ecf20Sopenharmony_ci			goto queue;
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	len = sizeof(*ga) + attr->dlen;
3108c2ecf20Sopenharmony_ci	if (skb_tailroom(app->pdu) < len)
3118c2ecf20Sopenharmony_ci		goto queue;
3128c2ecf20Sopenharmony_ci	ga = __skb_put(app->pdu, len);
3138c2ecf20Sopenharmony_ci	ga->len   = len;
3148c2ecf20Sopenharmony_ci	ga->event = event;
3158c2ecf20Sopenharmony_ci	memcpy(ga->data, attr->data, attr->dlen);
3168c2ecf20Sopenharmony_ci	return 0;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ciqueue:
3198c2ecf20Sopenharmony_ci	garp_pdu_queue(app);
3208c2ecf20Sopenharmony_ci	goto again;
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistatic void garp_attr_event(struct garp_applicant *app,
3248c2ecf20Sopenharmony_ci			    struct garp_attr *attr, enum garp_event event)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	enum garp_applicant_state state;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	state = garp_applicant_state_table[attr->state][event].state;
3298c2ecf20Sopenharmony_ci	if (state == GARP_APPLICANT_INVALID)
3308c2ecf20Sopenharmony_ci		return;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	switch (garp_applicant_state_table[attr->state][event].action) {
3338c2ecf20Sopenharmony_ci	case GARP_ACTION_NONE:
3348c2ecf20Sopenharmony_ci		break;
3358c2ecf20Sopenharmony_ci	case GARP_ACTION_S_JOIN_IN:
3368c2ecf20Sopenharmony_ci		/* When appending the attribute fails, don't update state in
3378c2ecf20Sopenharmony_ci		 * order to retry on next TRANSMIT_PDU event. */
3388c2ecf20Sopenharmony_ci		if (garp_pdu_append_attr(app, attr, GARP_JOIN_IN) < 0)
3398c2ecf20Sopenharmony_ci			return;
3408c2ecf20Sopenharmony_ci		break;
3418c2ecf20Sopenharmony_ci	case GARP_ACTION_S_LEAVE_EMPTY:
3428c2ecf20Sopenharmony_ci		garp_pdu_append_attr(app, attr, GARP_LEAVE_EMPTY);
3438c2ecf20Sopenharmony_ci		/* As a pure applicant, sending a leave message implies that
3448c2ecf20Sopenharmony_ci		 * the attribute was unregistered and can be destroyed. */
3458c2ecf20Sopenharmony_ci		garp_attr_destroy(app, attr);
3468c2ecf20Sopenharmony_ci		return;
3478c2ecf20Sopenharmony_ci	default:
3488c2ecf20Sopenharmony_ci		WARN_ON(1);
3498c2ecf20Sopenharmony_ci	}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	attr->state = state;
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ciint garp_request_join(const struct net_device *dev,
3558c2ecf20Sopenharmony_ci		      const struct garp_application *appl,
3568c2ecf20Sopenharmony_ci		      const void *data, u8 len, u8 type)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	struct garp_port *port = rtnl_dereference(dev->garp_port);
3598c2ecf20Sopenharmony_ci	struct garp_applicant *app = rtnl_dereference(port->applicants[appl->type]);
3608c2ecf20Sopenharmony_ci	struct garp_attr *attr;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	spin_lock_bh(&app->lock);
3638c2ecf20Sopenharmony_ci	attr = garp_attr_create(app, data, len, type);
3648c2ecf20Sopenharmony_ci	if (!attr) {
3658c2ecf20Sopenharmony_ci		spin_unlock_bh(&app->lock);
3668c2ecf20Sopenharmony_ci		return -ENOMEM;
3678c2ecf20Sopenharmony_ci	}
3688c2ecf20Sopenharmony_ci	garp_attr_event(app, attr, GARP_EVENT_REQ_JOIN);
3698c2ecf20Sopenharmony_ci	spin_unlock_bh(&app->lock);
3708c2ecf20Sopenharmony_ci	return 0;
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(garp_request_join);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_civoid garp_request_leave(const struct net_device *dev,
3758c2ecf20Sopenharmony_ci			const struct garp_application *appl,
3768c2ecf20Sopenharmony_ci			const void *data, u8 len, u8 type)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	struct garp_port *port = rtnl_dereference(dev->garp_port);
3798c2ecf20Sopenharmony_ci	struct garp_applicant *app = rtnl_dereference(port->applicants[appl->type]);
3808c2ecf20Sopenharmony_ci	struct garp_attr *attr;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	spin_lock_bh(&app->lock);
3838c2ecf20Sopenharmony_ci	attr = garp_attr_lookup(app, data, len, type);
3848c2ecf20Sopenharmony_ci	if (!attr) {
3858c2ecf20Sopenharmony_ci		spin_unlock_bh(&app->lock);
3868c2ecf20Sopenharmony_ci		return;
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci	garp_attr_event(app, attr, GARP_EVENT_REQ_LEAVE);
3898c2ecf20Sopenharmony_ci	spin_unlock_bh(&app->lock);
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(garp_request_leave);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic void garp_gid_event(struct garp_applicant *app, enum garp_event event)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	struct rb_node *node, *next;
3968c2ecf20Sopenharmony_ci	struct garp_attr *attr;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	for (node = rb_first(&app->gid);
3998c2ecf20Sopenharmony_ci	     next = node ? rb_next(node) : NULL, node != NULL;
4008c2ecf20Sopenharmony_ci	     node = next) {
4018c2ecf20Sopenharmony_ci		attr = rb_entry(node, struct garp_attr, node);
4028c2ecf20Sopenharmony_ci		garp_attr_event(app, attr, event);
4038c2ecf20Sopenharmony_ci	}
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_cistatic void garp_join_timer_arm(struct garp_applicant *app)
4078c2ecf20Sopenharmony_ci{
4088c2ecf20Sopenharmony_ci	unsigned long delay;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	delay = (u64)msecs_to_jiffies(garp_join_time) * prandom_u32() >> 32;
4118c2ecf20Sopenharmony_ci	mod_timer(&app->join_timer, jiffies + delay);
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_cistatic void garp_join_timer(struct timer_list *t)
4158c2ecf20Sopenharmony_ci{
4168c2ecf20Sopenharmony_ci	struct garp_applicant *app = from_timer(app, t, join_timer);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	spin_lock(&app->lock);
4198c2ecf20Sopenharmony_ci	garp_gid_event(app, GARP_EVENT_TRANSMIT_PDU);
4208c2ecf20Sopenharmony_ci	garp_pdu_queue(app);
4218c2ecf20Sopenharmony_ci	spin_unlock(&app->lock);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	garp_queue_xmit(app);
4248c2ecf20Sopenharmony_ci	garp_join_timer_arm(app);
4258c2ecf20Sopenharmony_ci}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_cistatic int garp_pdu_parse_end_mark(struct sk_buff *skb)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	if (!pskb_may_pull(skb, sizeof(u8)))
4308c2ecf20Sopenharmony_ci		return -1;
4318c2ecf20Sopenharmony_ci	if (*skb->data == GARP_END_MARK) {
4328c2ecf20Sopenharmony_ci		skb_pull(skb, sizeof(u8));
4338c2ecf20Sopenharmony_ci		return -1;
4348c2ecf20Sopenharmony_ci	}
4358c2ecf20Sopenharmony_ci	return 0;
4368c2ecf20Sopenharmony_ci}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_cistatic int garp_pdu_parse_attr(struct garp_applicant *app, struct sk_buff *skb,
4398c2ecf20Sopenharmony_ci			       u8 attrtype)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	const struct garp_attr_hdr *ga;
4428c2ecf20Sopenharmony_ci	struct garp_attr *attr;
4438c2ecf20Sopenharmony_ci	enum garp_event event;
4448c2ecf20Sopenharmony_ci	unsigned int dlen;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	if (!pskb_may_pull(skb, sizeof(*ga)))
4478c2ecf20Sopenharmony_ci		return -1;
4488c2ecf20Sopenharmony_ci	ga = (struct garp_attr_hdr *)skb->data;
4498c2ecf20Sopenharmony_ci	if (ga->len < sizeof(*ga))
4508c2ecf20Sopenharmony_ci		return -1;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	if (!pskb_may_pull(skb, ga->len))
4538c2ecf20Sopenharmony_ci		return -1;
4548c2ecf20Sopenharmony_ci	skb_pull(skb, ga->len);
4558c2ecf20Sopenharmony_ci	dlen = sizeof(*ga) - ga->len;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	if (attrtype > app->app->maxattr)
4588c2ecf20Sopenharmony_ci		return 0;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	switch (ga->event) {
4618c2ecf20Sopenharmony_ci	case GARP_LEAVE_ALL:
4628c2ecf20Sopenharmony_ci		if (dlen != 0)
4638c2ecf20Sopenharmony_ci			return -1;
4648c2ecf20Sopenharmony_ci		garp_gid_event(app, GARP_EVENT_R_LEAVE_EMPTY);
4658c2ecf20Sopenharmony_ci		return 0;
4668c2ecf20Sopenharmony_ci	case GARP_JOIN_EMPTY:
4678c2ecf20Sopenharmony_ci		event = GARP_EVENT_R_JOIN_EMPTY;
4688c2ecf20Sopenharmony_ci		break;
4698c2ecf20Sopenharmony_ci	case GARP_JOIN_IN:
4708c2ecf20Sopenharmony_ci		event = GARP_EVENT_R_JOIN_IN;
4718c2ecf20Sopenharmony_ci		break;
4728c2ecf20Sopenharmony_ci	case GARP_LEAVE_EMPTY:
4738c2ecf20Sopenharmony_ci		event = GARP_EVENT_R_LEAVE_EMPTY;
4748c2ecf20Sopenharmony_ci		break;
4758c2ecf20Sopenharmony_ci	case GARP_EMPTY:
4768c2ecf20Sopenharmony_ci		event = GARP_EVENT_R_EMPTY;
4778c2ecf20Sopenharmony_ci		break;
4788c2ecf20Sopenharmony_ci	default:
4798c2ecf20Sopenharmony_ci		return 0;
4808c2ecf20Sopenharmony_ci	}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	if (dlen == 0)
4838c2ecf20Sopenharmony_ci		return -1;
4848c2ecf20Sopenharmony_ci	attr = garp_attr_lookup(app, ga->data, dlen, attrtype);
4858c2ecf20Sopenharmony_ci	if (attr == NULL)
4868c2ecf20Sopenharmony_ci		return 0;
4878c2ecf20Sopenharmony_ci	garp_attr_event(app, attr, event);
4888c2ecf20Sopenharmony_ci	return 0;
4898c2ecf20Sopenharmony_ci}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_cistatic int garp_pdu_parse_msg(struct garp_applicant *app, struct sk_buff *skb)
4928c2ecf20Sopenharmony_ci{
4938c2ecf20Sopenharmony_ci	const struct garp_msg_hdr *gm;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	if (!pskb_may_pull(skb, sizeof(*gm)))
4968c2ecf20Sopenharmony_ci		return -1;
4978c2ecf20Sopenharmony_ci	gm = (struct garp_msg_hdr *)skb->data;
4988c2ecf20Sopenharmony_ci	if (gm->attrtype == 0)
4998c2ecf20Sopenharmony_ci		return -1;
5008c2ecf20Sopenharmony_ci	skb_pull(skb, sizeof(*gm));
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	while (skb->len > 0) {
5038c2ecf20Sopenharmony_ci		if (garp_pdu_parse_attr(app, skb, gm->attrtype) < 0)
5048c2ecf20Sopenharmony_ci			return -1;
5058c2ecf20Sopenharmony_ci		if (garp_pdu_parse_end_mark(skb) < 0)
5068c2ecf20Sopenharmony_ci			break;
5078c2ecf20Sopenharmony_ci	}
5088c2ecf20Sopenharmony_ci	return 0;
5098c2ecf20Sopenharmony_ci}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_cistatic void garp_pdu_rcv(const struct stp_proto *proto, struct sk_buff *skb,
5128c2ecf20Sopenharmony_ci			 struct net_device *dev)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	struct garp_application *appl = proto->data;
5158c2ecf20Sopenharmony_ci	struct garp_port *port;
5168c2ecf20Sopenharmony_ci	struct garp_applicant *app;
5178c2ecf20Sopenharmony_ci	const struct garp_pdu_hdr *gp;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	port = rcu_dereference(dev->garp_port);
5208c2ecf20Sopenharmony_ci	if (!port)
5218c2ecf20Sopenharmony_ci		goto err;
5228c2ecf20Sopenharmony_ci	app = rcu_dereference(port->applicants[appl->type]);
5238c2ecf20Sopenharmony_ci	if (!app)
5248c2ecf20Sopenharmony_ci		goto err;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	if (!pskb_may_pull(skb, sizeof(*gp)))
5278c2ecf20Sopenharmony_ci		goto err;
5288c2ecf20Sopenharmony_ci	gp = (struct garp_pdu_hdr *)skb->data;
5298c2ecf20Sopenharmony_ci	if (get_unaligned(&gp->protocol) != htons(GARP_PROTOCOL_ID))
5308c2ecf20Sopenharmony_ci		goto err;
5318c2ecf20Sopenharmony_ci	skb_pull(skb, sizeof(*gp));
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	spin_lock(&app->lock);
5348c2ecf20Sopenharmony_ci	while (skb->len > 0) {
5358c2ecf20Sopenharmony_ci		if (garp_pdu_parse_msg(app, skb) < 0)
5368c2ecf20Sopenharmony_ci			break;
5378c2ecf20Sopenharmony_ci		if (garp_pdu_parse_end_mark(skb) < 0)
5388c2ecf20Sopenharmony_ci			break;
5398c2ecf20Sopenharmony_ci	}
5408c2ecf20Sopenharmony_ci	spin_unlock(&app->lock);
5418c2ecf20Sopenharmony_cierr:
5428c2ecf20Sopenharmony_ci	kfree_skb(skb);
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_cistatic int garp_init_port(struct net_device *dev)
5468c2ecf20Sopenharmony_ci{
5478c2ecf20Sopenharmony_ci	struct garp_port *port;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	port = kzalloc(sizeof(*port), GFP_KERNEL);
5508c2ecf20Sopenharmony_ci	if (!port)
5518c2ecf20Sopenharmony_ci		return -ENOMEM;
5528c2ecf20Sopenharmony_ci	rcu_assign_pointer(dev->garp_port, port);
5538c2ecf20Sopenharmony_ci	return 0;
5548c2ecf20Sopenharmony_ci}
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_cistatic void garp_release_port(struct net_device *dev)
5578c2ecf20Sopenharmony_ci{
5588c2ecf20Sopenharmony_ci	struct garp_port *port = rtnl_dereference(dev->garp_port);
5598c2ecf20Sopenharmony_ci	unsigned int i;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	for (i = 0; i <= GARP_APPLICATION_MAX; i++) {
5628c2ecf20Sopenharmony_ci		if (rtnl_dereference(port->applicants[i]))
5638c2ecf20Sopenharmony_ci			return;
5648c2ecf20Sopenharmony_ci	}
5658c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(dev->garp_port, NULL);
5668c2ecf20Sopenharmony_ci	kfree_rcu(port, rcu);
5678c2ecf20Sopenharmony_ci}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ciint garp_init_applicant(struct net_device *dev, struct garp_application *appl)
5708c2ecf20Sopenharmony_ci{
5718c2ecf20Sopenharmony_ci	struct garp_applicant *app;
5728c2ecf20Sopenharmony_ci	int err;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	ASSERT_RTNL();
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	if (!rtnl_dereference(dev->garp_port)) {
5778c2ecf20Sopenharmony_ci		err = garp_init_port(dev);
5788c2ecf20Sopenharmony_ci		if (err < 0)
5798c2ecf20Sopenharmony_ci			goto err1;
5808c2ecf20Sopenharmony_ci	}
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	err = -ENOMEM;
5838c2ecf20Sopenharmony_ci	app = kzalloc(sizeof(*app), GFP_KERNEL);
5848c2ecf20Sopenharmony_ci	if (!app)
5858c2ecf20Sopenharmony_ci		goto err2;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	err = dev_mc_add(dev, appl->proto.group_address);
5888c2ecf20Sopenharmony_ci	if (err < 0)
5898c2ecf20Sopenharmony_ci		goto err3;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	app->dev = dev;
5928c2ecf20Sopenharmony_ci	app->app = appl;
5938c2ecf20Sopenharmony_ci	app->gid = RB_ROOT;
5948c2ecf20Sopenharmony_ci	spin_lock_init(&app->lock);
5958c2ecf20Sopenharmony_ci	skb_queue_head_init(&app->queue);
5968c2ecf20Sopenharmony_ci	rcu_assign_pointer(dev->garp_port->applicants[appl->type], app);
5978c2ecf20Sopenharmony_ci	timer_setup(&app->join_timer, garp_join_timer, 0);
5988c2ecf20Sopenharmony_ci	garp_join_timer_arm(app);
5998c2ecf20Sopenharmony_ci	return 0;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_cierr3:
6028c2ecf20Sopenharmony_ci	kfree(app);
6038c2ecf20Sopenharmony_cierr2:
6048c2ecf20Sopenharmony_ci	garp_release_port(dev);
6058c2ecf20Sopenharmony_cierr1:
6068c2ecf20Sopenharmony_ci	return err;
6078c2ecf20Sopenharmony_ci}
6088c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(garp_init_applicant);
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_civoid garp_uninit_applicant(struct net_device *dev, struct garp_application *appl)
6118c2ecf20Sopenharmony_ci{
6128c2ecf20Sopenharmony_ci	struct garp_port *port = rtnl_dereference(dev->garp_port);
6138c2ecf20Sopenharmony_ci	struct garp_applicant *app = rtnl_dereference(port->applicants[appl->type]);
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	ASSERT_RTNL();
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(port->applicants[appl->type], NULL);
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	/* Delete timer and generate a final TRANSMIT_PDU event to flush out
6208c2ecf20Sopenharmony_ci	 * all pending messages before the applicant is gone. */
6218c2ecf20Sopenharmony_ci	del_timer_sync(&app->join_timer);
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	spin_lock_bh(&app->lock);
6248c2ecf20Sopenharmony_ci	garp_gid_event(app, GARP_EVENT_TRANSMIT_PDU);
6258c2ecf20Sopenharmony_ci	garp_attr_destroy_all(app);
6268c2ecf20Sopenharmony_ci	garp_pdu_queue(app);
6278c2ecf20Sopenharmony_ci	spin_unlock_bh(&app->lock);
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	garp_queue_xmit(app);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	dev_mc_del(dev, appl->proto.group_address);
6328c2ecf20Sopenharmony_ci	kfree_rcu(app, rcu);
6338c2ecf20Sopenharmony_ci	garp_release_port(dev);
6348c2ecf20Sopenharmony_ci}
6358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(garp_uninit_applicant);
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ciint garp_register_application(struct garp_application *appl)
6388c2ecf20Sopenharmony_ci{
6398c2ecf20Sopenharmony_ci	appl->proto.rcv = garp_pdu_rcv;
6408c2ecf20Sopenharmony_ci	appl->proto.data = appl;
6418c2ecf20Sopenharmony_ci	return stp_proto_register(&appl->proto);
6428c2ecf20Sopenharmony_ci}
6438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(garp_register_application);
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_civoid garp_unregister_application(struct garp_application *appl)
6468c2ecf20Sopenharmony_ci{
6478c2ecf20Sopenharmony_ci	stp_proto_unregister(&appl->proto);
6488c2ecf20Sopenharmony_ci}
6498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(garp_unregister_application);
650