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