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