162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * IEEE 802.1Q Multiple Registration Protocol (MRP) 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2012 Massachusetts Institute of Technology 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Adapted from code in net/802/garp.c 862306a36Sopenharmony_ci * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/timer.h> 1262306a36Sopenharmony_ci#include <linux/skbuff.h> 1362306a36Sopenharmony_ci#include <linux/netdevice.h> 1462306a36Sopenharmony_ci#include <linux/etherdevice.h> 1562306a36Sopenharmony_ci#include <linux/rtnetlink.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <net/mrp.h> 1962306a36Sopenharmony_ci#include <asm/unaligned.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic unsigned int mrp_join_time __read_mostly = 200; 2262306a36Sopenharmony_cimodule_param(mrp_join_time, uint, 0644); 2362306a36Sopenharmony_ciMODULE_PARM_DESC(mrp_join_time, "Join time in ms (default 200ms)"); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic unsigned int mrp_periodic_time __read_mostly = 1000; 2662306a36Sopenharmony_cimodule_param(mrp_periodic_time, uint, 0644); 2762306a36Sopenharmony_ciMODULE_PARM_DESC(mrp_periodic_time, "Periodic time in ms (default 1s)"); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic const u8 3262306a36Sopenharmony_cimrp_applicant_state_table[MRP_APPLICANT_MAX + 1][MRP_EVENT_MAX + 1] = { 3362306a36Sopenharmony_ci [MRP_APPLICANT_VO] = { 3462306a36Sopenharmony_ci [MRP_EVENT_NEW] = MRP_APPLICANT_VN, 3562306a36Sopenharmony_ci [MRP_EVENT_JOIN] = MRP_APPLICANT_VP, 3662306a36Sopenharmony_ci [MRP_EVENT_LV] = MRP_APPLICANT_VO, 3762306a36Sopenharmony_ci [MRP_EVENT_TX] = MRP_APPLICANT_VO, 3862306a36Sopenharmony_ci [MRP_EVENT_R_NEW] = MRP_APPLICANT_VO, 3962306a36Sopenharmony_ci [MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_AO, 4062306a36Sopenharmony_ci [MRP_EVENT_R_IN] = MRP_APPLICANT_VO, 4162306a36Sopenharmony_ci [MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_VO, 4262306a36Sopenharmony_ci [MRP_EVENT_R_MT] = MRP_APPLICANT_VO, 4362306a36Sopenharmony_ci [MRP_EVENT_R_LV] = MRP_APPLICANT_VO, 4462306a36Sopenharmony_ci [MRP_EVENT_R_LA] = MRP_APPLICANT_VO, 4562306a36Sopenharmony_ci [MRP_EVENT_REDECLARE] = MRP_APPLICANT_VO, 4662306a36Sopenharmony_ci [MRP_EVENT_PERIODIC] = MRP_APPLICANT_VO, 4762306a36Sopenharmony_ci }, 4862306a36Sopenharmony_ci [MRP_APPLICANT_VP] = { 4962306a36Sopenharmony_ci [MRP_EVENT_NEW] = MRP_APPLICANT_VN, 5062306a36Sopenharmony_ci [MRP_EVENT_JOIN] = MRP_APPLICANT_VP, 5162306a36Sopenharmony_ci [MRP_EVENT_LV] = MRP_APPLICANT_VO, 5262306a36Sopenharmony_ci [MRP_EVENT_TX] = MRP_APPLICANT_AA, 5362306a36Sopenharmony_ci [MRP_EVENT_R_NEW] = MRP_APPLICANT_VP, 5462306a36Sopenharmony_ci [MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_AP, 5562306a36Sopenharmony_ci [MRP_EVENT_R_IN] = MRP_APPLICANT_VP, 5662306a36Sopenharmony_ci [MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_VP, 5762306a36Sopenharmony_ci [MRP_EVENT_R_MT] = MRP_APPLICANT_VP, 5862306a36Sopenharmony_ci [MRP_EVENT_R_LV] = MRP_APPLICANT_VP, 5962306a36Sopenharmony_ci [MRP_EVENT_R_LA] = MRP_APPLICANT_VP, 6062306a36Sopenharmony_ci [MRP_EVENT_REDECLARE] = MRP_APPLICANT_VP, 6162306a36Sopenharmony_ci [MRP_EVENT_PERIODIC] = MRP_APPLICANT_VP, 6262306a36Sopenharmony_ci }, 6362306a36Sopenharmony_ci [MRP_APPLICANT_VN] = { 6462306a36Sopenharmony_ci [MRP_EVENT_NEW] = MRP_APPLICANT_VN, 6562306a36Sopenharmony_ci [MRP_EVENT_JOIN] = MRP_APPLICANT_VN, 6662306a36Sopenharmony_ci [MRP_EVENT_LV] = MRP_APPLICANT_LA, 6762306a36Sopenharmony_ci [MRP_EVENT_TX] = MRP_APPLICANT_AN, 6862306a36Sopenharmony_ci [MRP_EVENT_R_NEW] = MRP_APPLICANT_VN, 6962306a36Sopenharmony_ci [MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_VN, 7062306a36Sopenharmony_ci [MRP_EVENT_R_IN] = MRP_APPLICANT_VN, 7162306a36Sopenharmony_ci [MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_VN, 7262306a36Sopenharmony_ci [MRP_EVENT_R_MT] = MRP_APPLICANT_VN, 7362306a36Sopenharmony_ci [MRP_EVENT_R_LV] = MRP_APPLICANT_VN, 7462306a36Sopenharmony_ci [MRP_EVENT_R_LA] = MRP_APPLICANT_VN, 7562306a36Sopenharmony_ci [MRP_EVENT_REDECLARE] = MRP_APPLICANT_VN, 7662306a36Sopenharmony_ci [MRP_EVENT_PERIODIC] = MRP_APPLICANT_VN, 7762306a36Sopenharmony_ci }, 7862306a36Sopenharmony_ci [MRP_APPLICANT_AN] = { 7962306a36Sopenharmony_ci [MRP_EVENT_NEW] = MRP_APPLICANT_AN, 8062306a36Sopenharmony_ci [MRP_EVENT_JOIN] = MRP_APPLICANT_AN, 8162306a36Sopenharmony_ci [MRP_EVENT_LV] = MRP_APPLICANT_LA, 8262306a36Sopenharmony_ci [MRP_EVENT_TX] = MRP_APPLICANT_QA, 8362306a36Sopenharmony_ci [MRP_EVENT_R_NEW] = MRP_APPLICANT_AN, 8462306a36Sopenharmony_ci [MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_AN, 8562306a36Sopenharmony_ci [MRP_EVENT_R_IN] = MRP_APPLICANT_AN, 8662306a36Sopenharmony_ci [MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_AN, 8762306a36Sopenharmony_ci [MRP_EVENT_R_MT] = MRP_APPLICANT_AN, 8862306a36Sopenharmony_ci [MRP_EVENT_R_LV] = MRP_APPLICANT_VN, 8962306a36Sopenharmony_ci [MRP_EVENT_R_LA] = MRP_APPLICANT_VN, 9062306a36Sopenharmony_ci [MRP_EVENT_REDECLARE] = MRP_APPLICANT_VN, 9162306a36Sopenharmony_ci [MRP_EVENT_PERIODIC] = MRP_APPLICANT_AN, 9262306a36Sopenharmony_ci }, 9362306a36Sopenharmony_ci [MRP_APPLICANT_AA] = { 9462306a36Sopenharmony_ci [MRP_EVENT_NEW] = MRP_APPLICANT_VN, 9562306a36Sopenharmony_ci [MRP_EVENT_JOIN] = MRP_APPLICANT_AA, 9662306a36Sopenharmony_ci [MRP_EVENT_LV] = MRP_APPLICANT_LA, 9762306a36Sopenharmony_ci [MRP_EVENT_TX] = MRP_APPLICANT_QA, 9862306a36Sopenharmony_ci [MRP_EVENT_R_NEW] = MRP_APPLICANT_AA, 9962306a36Sopenharmony_ci [MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_QA, 10062306a36Sopenharmony_ci [MRP_EVENT_R_IN] = MRP_APPLICANT_AA, 10162306a36Sopenharmony_ci [MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_AA, 10262306a36Sopenharmony_ci [MRP_EVENT_R_MT] = MRP_APPLICANT_AA, 10362306a36Sopenharmony_ci [MRP_EVENT_R_LV] = MRP_APPLICANT_VP, 10462306a36Sopenharmony_ci [MRP_EVENT_R_LA] = MRP_APPLICANT_VP, 10562306a36Sopenharmony_ci [MRP_EVENT_REDECLARE] = MRP_APPLICANT_VP, 10662306a36Sopenharmony_ci [MRP_EVENT_PERIODIC] = MRP_APPLICANT_AA, 10762306a36Sopenharmony_ci }, 10862306a36Sopenharmony_ci [MRP_APPLICANT_QA] = { 10962306a36Sopenharmony_ci [MRP_EVENT_NEW] = MRP_APPLICANT_VN, 11062306a36Sopenharmony_ci [MRP_EVENT_JOIN] = MRP_APPLICANT_QA, 11162306a36Sopenharmony_ci [MRP_EVENT_LV] = MRP_APPLICANT_LA, 11262306a36Sopenharmony_ci [MRP_EVENT_TX] = MRP_APPLICANT_QA, 11362306a36Sopenharmony_ci [MRP_EVENT_R_NEW] = MRP_APPLICANT_QA, 11462306a36Sopenharmony_ci [MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_QA, 11562306a36Sopenharmony_ci [MRP_EVENT_R_IN] = MRP_APPLICANT_QA, 11662306a36Sopenharmony_ci [MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_AA, 11762306a36Sopenharmony_ci [MRP_EVENT_R_MT] = MRP_APPLICANT_AA, 11862306a36Sopenharmony_ci [MRP_EVENT_R_LV] = MRP_APPLICANT_VP, 11962306a36Sopenharmony_ci [MRP_EVENT_R_LA] = MRP_APPLICANT_VP, 12062306a36Sopenharmony_ci [MRP_EVENT_REDECLARE] = MRP_APPLICANT_VP, 12162306a36Sopenharmony_ci [MRP_EVENT_PERIODIC] = MRP_APPLICANT_AA, 12262306a36Sopenharmony_ci }, 12362306a36Sopenharmony_ci [MRP_APPLICANT_LA] = { 12462306a36Sopenharmony_ci [MRP_EVENT_NEW] = MRP_APPLICANT_VN, 12562306a36Sopenharmony_ci [MRP_EVENT_JOIN] = MRP_APPLICANT_AA, 12662306a36Sopenharmony_ci [MRP_EVENT_LV] = MRP_APPLICANT_LA, 12762306a36Sopenharmony_ci [MRP_EVENT_TX] = MRP_APPLICANT_VO, 12862306a36Sopenharmony_ci [MRP_EVENT_R_NEW] = MRP_APPLICANT_LA, 12962306a36Sopenharmony_ci [MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_LA, 13062306a36Sopenharmony_ci [MRP_EVENT_R_IN] = MRP_APPLICANT_LA, 13162306a36Sopenharmony_ci [MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_LA, 13262306a36Sopenharmony_ci [MRP_EVENT_R_MT] = MRP_APPLICANT_LA, 13362306a36Sopenharmony_ci [MRP_EVENT_R_LV] = MRP_APPLICANT_LA, 13462306a36Sopenharmony_ci [MRP_EVENT_R_LA] = MRP_APPLICANT_LA, 13562306a36Sopenharmony_ci [MRP_EVENT_REDECLARE] = MRP_APPLICANT_LA, 13662306a36Sopenharmony_ci [MRP_EVENT_PERIODIC] = MRP_APPLICANT_LA, 13762306a36Sopenharmony_ci }, 13862306a36Sopenharmony_ci [MRP_APPLICANT_AO] = { 13962306a36Sopenharmony_ci [MRP_EVENT_NEW] = MRP_APPLICANT_VN, 14062306a36Sopenharmony_ci [MRP_EVENT_JOIN] = MRP_APPLICANT_AP, 14162306a36Sopenharmony_ci [MRP_EVENT_LV] = MRP_APPLICANT_AO, 14262306a36Sopenharmony_ci [MRP_EVENT_TX] = MRP_APPLICANT_AO, 14362306a36Sopenharmony_ci [MRP_EVENT_R_NEW] = MRP_APPLICANT_AO, 14462306a36Sopenharmony_ci [MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_QO, 14562306a36Sopenharmony_ci [MRP_EVENT_R_IN] = MRP_APPLICANT_AO, 14662306a36Sopenharmony_ci [MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_AO, 14762306a36Sopenharmony_ci [MRP_EVENT_R_MT] = MRP_APPLICANT_AO, 14862306a36Sopenharmony_ci [MRP_EVENT_R_LV] = MRP_APPLICANT_VO, 14962306a36Sopenharmony_ci [MRP_EVENT_R_LA] = MRP_APPLICANT_VO, 15062306a36Sopenharmony_ci [MRP_EVENT_REDECLARE] = MRP_APPLICANT_VO, 15162306a36Sopenharmony_ci [MRP_EVENT_PERIODIC] = MRP_APPLICANT_AO, 15262306a36Sopenharmony_ci }, 15362306a36Sopenharmony_ci [MRP_APPLICANT_QO] = { 15462306a36Sopenharmony_ci [MRP_EVENT_NEW] = MRP_APPLICANT_VN, 15562306a36Sopenharmony_ci [MRP_EVENT_JOIN] = MRP_APPLICANT_QP, 15662306a36Sopenharmony_ci [MRP_EVENT_LV] = MRP_APPLICANT_QO, 15762306a36Sopenharmony_ci [MRP_EVENT_TX] = MRP_APPLICANT_QO, 15862306a36Sopenharmony_ci [MRP_EVENT_R_NEW] = MRP_APPLICANT_QO, 15962306a36Sopenharmony_ci [MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_QO, 16062306a36Sopenharmony_ci [MRP_EVENT_R_IN] = MRP_APPLICANT_QO, 16162306a36Sopenharmony_ci [MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_AO, 16262306a36Sopenharmony_ci [MRP_EVENT_R_MT] = MRP_APPLICANT_AO, 16362306a36Sopenharmony_ci [MRP_EVENT_R_LV] = MRP_APPLICANT_VO, 16462306a36Sopenharmony_ci [MRP_EVENT_R_LA] = MRP_APPLICANT_VO, 16562306a36Sopenharmony_ci [MRP_EVENT_REDECLARE] = MRP_APPLICANT_VO, 16662306a36Sopenharmony_ci [MRP_EVENT_PERIODIC] = MRP_APPLICANT_QO, 16762306a36Sopenharmony_ci }, 16862306a36Sopenharmony_ci [MRP_APPLICANT_AP] = { 16962306a36Sopenharmony_ci [MRP_EVENT_NEW] = MRP_APPLICANT_VN, 17062306a36Sopenharmony_ci [MRP_EVENT_JOIN] = MRP_APPLICANT_AP, 17162306a36Sopenharmony_ci [MRP_EVENT_LV] = MRP_APPLICANT_AO, 17262306a36Sopenharmony_ci [MRP_EVENT_TX] = MRP_APPLICANT_QA, 17362306a36Sopenharmony_ci [MRP_EVENT_R_NEW] = MRP_APPLICANT_AP, 17462306a36Sopenharmony_ci [MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_QP, 17562306a36Sopenharmony_ci [MRP_EVENT_R_IN] = MRP_APPLICANT_AP, 17662306a36Sopenharmony_ci [MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_AP, 17762306a36Sopenharmony_ci [MRP_EVENT_R_MT] = MRP_APPLICANT_AP, 17862306a36Sopenharmony_ci [MRP_EVENT_R_LV] = MRP_APPLICANT_VP, 17962306a36Sopenharmony_ci [MRP_EVENT_R_LA] = MRP_APPLICANT_VP, 18062306a36Sopenharmony_ci [MRP_EVENT_REDECLARE] = MRP_APPLICANT_VP, 18162306a36Sopenharmony_ci [MRP_EVENT_PERIODIC] = MRP_APPLICANT_AP, 18262306a36Sopenharmony_ci }, 18362306a36Sopenharmony_ci [MRP_APPLICANT_QP] = { 18462306a36Sopenharmony_ci [MRP_EVENT_NEW] = MRP_APPLICANT_VN, 18562306a36Sopenharmony_ci [MRP_EVENT_JOIN] = MRP_APPLICANT_QP, 18662306a36Sopenharmony_ci [MRP_EVENT_LV] = MRP_APPLICANT_QO, 18762306a36Sopenharmony_ci [MRP_EVENT_TX] = MRP_APPLICANT_QP, 18862306a36Sopenharmony_ci [MRP_EVENT_R_NEW] = MRP_APPLICANT_QP, 18962306a36Sopenharmony_ci [MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_QP, 19062306a36Sopenharmony_ci [MRP_EVENT_R_IN] = MRP_APPLICANT_QP, 19162306a36Sopenharmony_ci [MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_AP, 19262306a36Sopenharmony_ci [MRP_EVENT_R_MT] = MRP_APPLICANT_AP, 19362306a36Sopenharmony_ci [MRP_EVENT_R_LV] = MRP_APPLICANT_VP, 19462306a36Sopenharmony_ci [MRP_EVENT_R_LA] = MRP_APPLICANT_VP, 19562306a36Sopenharmony_ci [MRP_EVENT_REDECLARE] = MRP_APPLICANT_VP, 19662306a36Sopenharmony_ci [MRP_EVENT_PERIODIC] = MRP_APPLICANT_AP, 19762306a36Sopenharmony_ci }, 19862306a36Sopenharmony_ci}; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic const u8 20162306a36Sopenharmony_cimrp_tx_action_table[MRP_APPLICANT_MAX + 1] = { 20262306a36Sopenharmony_ci [MRP_APPLICANT_VO] = MRP_TX_ACTION_S_IN_OPTIONAL, 20362306a36Sopenharmony_ci [MRP_APPLICANT_VP] = MRP_TX_ACTION_S_JOIN_IN, 20462306a36Sopenharmony_ci [MRP_APPLICANT_VN] = MRP_TX_ACTION_S_NEW, 20562306a36Sopenharmony_ci [MRP_APPLICANT_AN] = MRP_TX_ACTION_S_NEW, 20662306a36Sopenharmony_ci [MRP_APPLICANT_AA] = MRP_TX_ACTION_S_JOIN_IN, 20762306a36Sopenharmony_ci [MRP_APPLICANT_QA] = MRP_TX_ACTION_S_JOIN_IN_OPTIONAL, 20862306a36Sopenharmony_ci [MRP_APPLICANT_LA] = MRP_TX_ACTION_S_LV, 20962306a36Sopenharmony_ci [MRP_APPLICANT_AO] = MRP_TX_ACTION_S_IN_OPTIONAL, 21062306a36Sopenharmony_ci [MRP_APPLICANT_QO] = MRP_TX_ACTION_S_IN_OPTIONAL, 21162306a36Sopenharmony_ci [MRP_APPLICANT_AP] = MRP_TX_ACTION_S_JOIN_IN, 21262306a36Sopenharmony_ci [MRP_APPLICANT_QP] = MRP_TX_ACTION_S_IN_OPTIONAL, 21362306a36Sopenharmony_ci}; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic void mrp_attrvalue_inc(void *value, u8 len) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci u8 *v = (u8 *)value; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* Add 1 to the last byte. If it becomes zero, 22062306a36Sopenharmony_ci * go to the previous byte and repeat. 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_ci while (len > 0 && !++v[--len]) 22362306a36Sopenharmony_ci ; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic int mrp_attr_cmp(const struct mrp_attr *attr, 22762306a36Sopenharmony_ci const void *value, u8 len, u8 type) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci if (attr->type != type) 23062306a36Sopenharmony_ci return attr->type - type; 23162306a36Sopenharmony_ci if (attr->len != len) 23262306a36Sopenharmony_ci return attr->len - len; 23362306a36Sopenharmony_ci return memcmp(attr->value, value, len); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic struct mrp_attr *mrp_attr_lookup(const struct mrp_applicant *app, 23762306a36Sopenharmony_ci const void *value, u8 len, u8 type) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci struct rb_node *parent = app->mad.rb_node; 24062306a36Sopenharmony_ci struct mrp_attr *attr; 24162306a36Sopenharmony_ci int d; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci while (parent) { 24462306a36Sopenharmony_ci attr = rb_entry(parent, struct mrp_attr, node); 24562306a36Sopenharmony_ci d = mrp_attr_cmp(attr, value, len, type); 24662306a36Sopenharmony_ci if (d > 0) 24762306a36Sopenharmony_ci parent = parent->rb_left; 24862306a36Sopenharmony_ci else if (d < 0) 24962306a36Sopenharmony_ci parent = parent->rb_right; 25062306a36Sopenharmony_ci else 25162306a36Sopenharmony_ci return attr; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci return NULL; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic struct mrp_attr *mrp_attr_create(struct mrp_applicant *app, 25762306a36Sopenharmony_ci const void *value, u8 len, u8 type) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci struct rb_node *parent = NULL, **p = &app->mad.rb_node; 26062306a36Sopenharmony_ci struct mrp_attr *attr; 26162306a36Sopenharmony_ci int d; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci while (*p) { 26462306a36Sopenharmony_ci parent = *p; 26562306a36Sopenharmony_ci attr = rb_entry(parent, struct mrp_attr, node); 26662306a36Sopenharmony_ci d = mrp_attr_cmp(attr, value, len, type); 26762306a36Sopenharmony_ci if (d > 0) 26862306a36Sopenharmony_ci p = &parent->rb_left; 26962306a36Sopenharmony_ci else if (d < 0) 27062306a36Sopenharmony_ci p = &parent->rb_right; 27162306a36Sopenharmony_ci else { 27262306a36Sopenharmony_ci /* The attribute already exists; re-use it. */ 27362306a36Sopenharmony_ci return attr; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci attr = kmalloc(sizeof(*attr) + len, GFP_ATOMIC); 27762306a36Sopenharmony_ci if (!attr) 27862306a36Sopenharmony_ci return attr; 27962306a36Sopenharmony_ci attr->state = MRP_APPLICANT_VO; 28062306a36Sopenharmony_ci attr->type = type; 28162306a36Sopenharmony_ci attr->len = len; 28262306a36Sopenharmony_ci memcpy(attr->value, value, len); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci rb_link_node(&attr->node, parent, p); 28562306a36Sopenharmony_ci rb_insert_color(&attr->node, &app->mad); 28662306a36Sopenharmony_ci return attr; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic void mrp_attr_destroy(struct mrp_applicant *app, struct mrp_attr *attr) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci rb_erase(&attr->node, &app->mad); 29262306a36Sopenharmony_ci kfree(attr); 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic void mrp_attr_destroy_all(struct mrp_applicant *app) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci struct rb_node *node, *next; 29862306a36Sopenharmony_ci struct mrp_attr *attr; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci for (node = rb_first(&app->mad); 30162306a36Sopenharmony_ci next = node ? rb_next(node) : NULL, node != NULL; 30262306a36Sopenharmony_ci node = next) { 30362306a36Sopenharmony_ci attr = rb_entry(node, struct mrp_attr, node); 30462306a36Sopenharmony_ci mrp_attr_destroy(app, attr); 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic int mrp_pdu_init(struct mrp_applicant *app) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci struct sk_buff *skb; 31162306a36Sopenharmony_ci struct mrp_pdu_hdr *ph; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci skb = alloc_skb(app->dev->mtu + LL_RESERVED_SPACE(app->dev), 31462306a36Sopenharmony_ci GFP_ATOMIC); 31562306a36Sopenharmony_ci if (!skb) 31662306a36Sopenharmony_ci return -ENOMEM; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci skb->dev = app->dev; 31962306a36Sopenharmony_ci skb->protocol = app->app->pkttype.type; 32062306a36Sopenharmony_ci skb_reserve(skb, LL_RESERVED_SPACE(app->dev)); 32162306a36Sopenharmony_ci skb_reset_network_header(skb); 32262306a36Sopenharmony_ci skb_reset_transport_header(skb); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci ph = __skb_put(skb, sizeof(*ph)); 32562306a36Sopenharmony_ci ph->version = app->app->version; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci app->pdu = skb; 32862306a36Sopenharmony_ci return 0; 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic int mrp_pdu_append_end_mark(struct mrp_applicant *app) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci __be16 *endmark; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci if (skb_tailroom(app->pdu) < sizeof(*endmark)) 33662306a36Sopenharmony_ci return -1; 33762306a36Sopenharmony_ci endmark = __skb_put(app->pdu, sizeof(*endmark)); 33862306a36Sopenharmony_ci put_unaligned(MRP_END_MARK, endmark); 33962306a36Sopenharmony_ci return 0; 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic void mrp_pdu_queue(struct mrp_applicant *app) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci if (!app->pdu) 34562306a36Sopenharmony_ci return; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (mrp_cb(app->pdu)->mh) 34862306a36Sopenharmony_ci mrp_pdu_append_end_mark(app); 34962306a36Sopenharmony_ci mrp_pdu_append_end_mark(app); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci dev_hard_header(app->pdu, app->dev, ntohs(app->app->pkttype.type), 35262306a36Sopenharmony_ci app->app->group_address, app->dev->dev_addr, 35362306a36Sopenharmony_ci app->pdu->len); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci skb_queue_tail(&app->queue, app->pdu); 35662306a36Sopenharmony_ci app->pdu = NULL; 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic void mrp_queue_xmit(struct mrp_applicant *app) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct sk_buff *skb; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci while ((skb = skb_dequeue(&app->queue))) 36462306a36Sopenharmony_ci dev_queue_xmit(skb); 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic int mrp_pdu_append_msg_hdr(struct mrp_applicant *app, 36862306a36Sopenharmony_ci u8 attrtype, u8 attrlen) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci struct mrp_msg_hdr *mh; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (mrp_cb(app->pdu)->mh) { 37362306a36Sopenharmony_ci if (mrp_pdu_append_end_mark(app) < 0) 37462306a36Sopenharmony_ci return -1; 37562306a36Sopenharmony_ci mrp_cb(app->pdu)->mh = NULL; 37662306a36Sopenharmony_ci mrp_cb(app->pdu)->vah = NULL; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (skb_tailroom(app->pdu) < sizeof(*mh)) 38062306a36Sopenharmony_ci return -1; 38162306a36Sopenharmony_ci mh = __skb_put(app->pdu, sizeof(*mh)); 38262306a36Sopenharmony_ci mh->attrtype = attrtype; 38362306a36Sopenharmony_ci mh->attrlen = attrlen; 38462306a36Sopenharmony_ci mrp_cb(app->pdu)->mh = mh; 38562306a36Sopenharmony_ci return 0; 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic int mrp_pdu_append_vecattr_hdr(struct mrp_applicant *app, 38962306a36Sopenharmony_ci const void *firstattrvalue, u8 attrlen) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci struct mrp_vecattr_hdr *vah; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (skb_tailroom(app->pdu) < sizeof(*vah) + attrlen) 39462306a36Sopenharmony_ci return -1; 39562306a36Sopenharmony_ci vah = __skb_put(app->pdu, sizeof(*vah) + attrlen); 39662306a36Sopenharmony_ci put_unaligned(0, &vah->lenflags); 39762306a36Sopenharmony_ci memcpy(vah->firstattrvalue, firstattrvalue, attrlen); 39862306a36Sopenharmony_ci mrp_cb(app->pdu)->vah = vah; 39962306a36Sopenharmony_ci memcpy(mrp_cb(app->pdu)->attrvalue, firstattrvalue, attrlen); 40062306a36Sopenharmony_ci return 0; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic int mrp_pdu_append_vecattr_event(struct mrp_applicant *app, 40462306a36Sopenharmony_ci const struct mrp_attr *attr, 40562306a36Sopenharmony_ci enum mrp_vecattr_event vaevent) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci u16 len, pos; 40862306a36Sopenharmony_ci u8 *vaevents; 40962306a36Sopenharmony_ci int err; 41062306a36Sopenharmony_ciagain: 41162306a36Sopenharmony_ci if (!app->pdu) { 41262306a36Sopenharmony_ci err = mrp_pdu_init(app); 41362306a36Sopenharmony_ci if (err < 0) 41462306a36Sopenharmony_ci return err; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* If there is no Message header in the PDU, or the Message header is 41862306a36Sopenharmony_ci * for a different attribute type, add an EndMark (if necessary) and a 41962306a36Sopenharmony_ci * new Message header to the PDU. 42062306a36Sopenharmony_ci */ 42162306a36Sopenharmony_ci if (!mrp_cb(app->pdu)->mh || 42262306a36Sopenharmony_ci mrp_cb(app->pdu)->mh->attrtype != attr->type || 42362306a36Sopenharmony_ci mrp_cb(app->pdu)->mh->attrlen != attr->len) { 42462306a36Sopenharmony_ci if (mrp_pdu_append_msg_hdr(app, attr->type, attr->len) < 0) 42562306a36Sopenharmony_ci goto queue; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci /* If there is no VectorAttribute header for this Message in the PDU, 42962306a36Sopenharmony_ci * or this attribute's value does not sequentially follow the previous 43062306a36Sopenharmony_ci * attribute's value, add a new VectorAttribute header to the PDU. 43162306a36Sopenharmony_ci */ 43262306a36Sopenharmony_ci if (!mrp_cb(app->pdu)->vah || 43362306a36Sopenharmony_ci memcmp(mrp_cb(app->pdu)->attrvalue, attr->value, attr->len)) { 43462306a36Sopenharmony_ci if (mrp_pdu_append_vecattr_hdr(app, attr->value, attr->len) < 0) 43562306a36Sopenharmony_ci goto queue; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci len = be16_to_cpu(get_unaligned(&mrp_cb(app->pdu)->vah->lenflags)); 43962306a36Sopenharmony_ci pos = len % 3; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci /* Events are packed into Vectors in the PDU, three to a byte. Add a 44262306a36Sopenharmony_ci * byte to the end of the Vector if necessary. 44362306a36Sopenharmony_ci */ 44462306a36Sopenharmony_ci if (!pos) { 44562306a36Sopenharmony_ci if (skb_tailroom(app->pdu) < sizeof(u8)) 44662306a36Sopenharmony_ci goto queue; 44762306a36Sopenharmony_ci vaevents = __skb_put(app->pdu, sizeof(u8)); 44862306a36Sopenharmony_ci } else { 44962306a36Sopenharmony_ci vaevents = (u8 *)(skb_tail_pointer(app->pdu) - sizeof(u8)); 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci switch (pos) { 45362306a36Sopenharmony_ci case 0: 45462306a36Sopenharmony_ci *vaevents = vaevent * (__MRP_VECATTR_EVENT_MAX * 45562306a36Sopenharmony_ci __MRP_VECATTR_EVENT_MAX); 45662306a36Sopenharmony_ci break; 45762306a36Sopenharmony_ci case 1: 45862306a36Sopenharmony_ci *vaevents += vaevent * __MRP_VECATTR_EVENT_MAX; 45962306a36Sopenharmony_ci break; 46062306a36Sopenharmony_ci case 2: 46162306a36Sopenharmony_ci *vaevents += vaevent; 46262306a36Sopenharmony_ci break; 46362306a36Sopenharmony_ci default: 46462306a36Sopenharmony_ci WARN_ON(1); 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* Increment the length of the VectorAttribute in the PDU, as well as 46862306a36Sopenharmony_ci * the value of the next attribute that would continue its Vector. 46962306a36Sopenharmony_ci */ 47062306a36Sopenharmony_ci put_unaligned(cpu_to_be16(++len), &mrp_cb(app->pdu)->vah->lenflags); 47162306a36Sopenharmony_ci mrp_attrvalue_inc(mrp_cb(app->pdu)->attrvalue, attr->len); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci return 0; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ciqueue: 47662306a36Sopenharmony_ci mrp_pdu_queue(app); 47762306a36Sopenharmony_ci goto again; 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic void mrp_attr_event(struct mrp_applicant *app, 48162306a36Sopenharmony_ci struct mrp_attr *attr, enum mrp_event event) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci enum mrp_applicant_state state; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci state = mrp_applicant_state_table[attr->state][event]; 48662306a36Sopenharmony_ci if (state == MRP_APPLICANT_INVALID) { 48762306a36Sopenharmony_ci WARN_ON(1); 48862306a36Sopenharmony_ci return; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (event == MRP_EVENT_TX) { 49262306a36Sopenharmony_ci /* When appending the attribute fails, don't update its state 49362306a36Sopenharmony_ci * in order to retry at the next TX event. 49462306a36Sopenharmony_ci */ 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci switch (mrp_tx_action_table[attr->state]) { 49762306a36Sopenharmony_ci case MRP_TX_ACTION_NONE: 49862306a36Sopenharmony_ci case MRP_TX_ACTION_S_JOIN_IN_OPTIONAL: 49962306a36Sopenharmony_ci case MRP_TX_ACTION_S_IN_OPTIONAL: 50062306a36Sopenharmony_ci break; 50162306a36Sopenharmony_ci case MRP_TX_ACTION_S_NEW: 50262306a36Sopenharmony_ci if (mrp_pdu_append_vecattr_event( 50362306a36Sopenharmony_ci app, attr, MRP_VECATTR_EVENT_NEW) < 0) 50462306a36Sopenharmony_ci return; 50562306a36Sopenharmony_ci break; 50662306a36Sopenharmony_ci case MRP_TX_ACTION_S_JOIN_IN: 50762306a36Sopenharmony_ci if (mrp_pdu_append_vecattr_event( 50862306a36Sopenharmony_ci app, attr, MRP_VECATTR_EVENT_JOIN_IN) < 0) 50962306a36Sopenharmony_ci return; 51062306a36Sopenharmony_ci break; 51162306a36Sopenharmony_ci case MRP_TX_ACTION_S_LV: 51262306a36Sopenharmony_ci if (mrp_pdu_append_vecattr_event( 51362306a36Sopenharmony_ci app, attr, MRP_VECATTR_EVENT_LV) < 0) 51462306a36Sopenharmony_ci return; 51562306a36Sopenharmony_ci /* As a pure applicant, sending a leave message 51662306a36Sopenharmony_ci * implies that the attribute was unregistered and 51762306a36Sopenharmony_ci * can be destroyed. 51862306a36Sopenharmony_ci */ 51962306a36Sopenharmony_ci mrp_attr_destroy(app, attr); 52062306a36Sopenharmony_ci return; 52162306a36Sopenharmony_ci default: 52262306a36Sopenharmony_ci WARN_ON(1); 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci attr->state = state; 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ciint mrp_request_join(const struct net_device *dev, 53062306a36Sopenharmony_ci const struct mrp_application *appl, 53162306a36Sopenharmony_ci const void *value, u8 len, u8 type) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci struct mrp_port *port = rtnl_dereference(dev->mrp_port); 53462306a36Sopenharmony_ci struct mrp_applicant *app = rtnl_dereference( 53562306a36Sopenharmony_ci port->applicants[appl->type]); 53662306a36Sopenharmony_ci struct mrp_attr *attr; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci if (sizeof(struct mrp_skb_cb) + len > 53962306a36Sopenharmony_ci sizeof_field(struct sk_buff, cb)) 54062306a36Sopenharmony_ci return -ENOMEM; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci spin_lock_bh(&app->lock); 54362306a36Sopenharmony_ci attr = mrp_attr_create(app, value, len, type); 54462306a36Sopenharmony_ci if (!attr) { 54562306a36Sopenharmony_ci spin_unlock_bh(&app->lock); 54662306a36Sopenharmony_ci return -ENOMEM; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci mrp_attr_event(app, attr, MRP_EVENT_JOIN); 54962306a36Sopenharmony_ci spin_unlock_bh(&app->lock); 55062306a36Sopenharmony_ci return 0; 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mrp_request_join); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_civoid mrp_request_leave(const struct net_device *dev, 55562306a36Sopenharmony_ci const struct mrp_application *appl, 55662306a36Sopenharmony_ci const void *value, u8 len, u8 type) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci struct mrp_port *port = rtnl_dereference(dev->mrp_port); 55962306a36Sopenharmony_ci struct mrp_applicant *app = rtnl_dereference( 56062306a36Sopenharmony_ci port->applicants[appl->type]); 56162306a36Sopenharmony_ci struct mrp_attr *attr; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci if (sizeof(struct mrp_skb_cb) + len > 56462306a36Sopenharmony_ci sizeof_field(struct sk_buff, cb)) 56562306a36Sopenharmony_ci return; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci spin_lock_bh(&app->lock); 56862306a36Sopenharmony_ci attr = mrp_attr_lookup(app, value, len, type); 56962306a36Sopenharmony_ci if (!attr) { 57062306a36Sopenharmony_ci spin_unlock_bh(&app->lock); 57162306a36Sopenharmony_ci return; 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci mrp_attr_event(app, attr, MRP_EVENT_LV); 57462306a36Sopenharmony_ci spin_unlock_bh(&app->lock); 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mrp_request_leave); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cistatic void mrp_mad_event(struct mrp_applicant *app, enum mrp_event event) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci struct rb_node *node, *next; 58162306a36Sopenharmony_ci struct mrp_attr *attr; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci for (node = rb_first(&app->mad); 58462306a36Sopenharmony_ci next = node ? rb_next(node) : NULL, node != NULL; 58562306a36Sopenharmony_ci node = next) { 58662306a36Sopenharmony_ci attr = rb_entry(node, struct mrp_attr, node); 58762306a36Sopenharmony_ci mrp_attr_event(app, attr, event); 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic void mrp_join_timer_arm(struct mrp_applicant *app) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci unsigned long delay; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci delay = get_random_u32_below(msecs_to_jiffies(mrp_join_time)); 59662306a36Sopenharmony_ci mod_timer(&app->join_timer, jiffies + delay); 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_cistatic void mrp_join_timer(struct timer_list *t) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci struct mrp_applicant *app = from_timer(app, t, join_timer); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci spin_lock(&app->lock); 60462306a36Sopenharmony_ci mrp_mad_event(app, MRP_EVENT_TX); 60562306a36Sopenharmony_ci mrp_pdu_queue(app); 60662306a36Sopenharmony_ci spin_unlock(&app->lock); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci mrp_queue_xmit(app); 60962306a36Sopenharmony_ci spin_lock(&app->lock); 61062306a36Sopenharmony_ci if (likely(app->active)) 61162306a36Sopenharmony_ci mrp_join_timer_arm(app); 61262306a36Sopenharmony_ci spin_unlock(&app->lock); 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_cistatic void mrp_periodic_timer_arm(struct mrp_applicant *app) 61662306a36Sopenharmony_ci{ 61762306a36Sopenharmony_ci mod_timer(&app->periodic_timer, 61862306a36Sopenharmony_ci jiffies + msecs_to_jiffies(mrp_periodic_time)); 61962306a36Sopenharmony_ci} 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cistatic void mrp_periodic_timer(struct timer_list *t) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci struct mrp_applicant *app = from_timer(app, t, periodic_timer); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci spin_lock(&app->lock); 62662306a36Sopenharmony_ci if (likely(app->active)) { 62762306a36Sopenharmony_ci mrp_mad_event(app, MRP_EVENT_PERIODIC); 62862306a36Sopenharmony_ci mrp_pdu_queue(app); 62962306a36Sopenharmony_ci mrp_periodic_timer_arm(app); 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci spin_unlock(&app->lock); 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_cistatic int mrp_pdu_parse_end_mark(struct sk_buff *skb, int *offset) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci __be16 endmark; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci if (skb_copy_bits(skb, *offset, &endmark, sizeof(endmark)) < 0) 63962306a36Sopenharmony_ci return -1; 64062306a36Sopenharmony_ci if (endmark == MRP_END_MARK) { 64162306a36Sopenharmony_ci *offset += sizeof(endmark); 64262306a36Sopenharmony_ci return -1; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci return 0; 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic void mrp_pdu_parse_vecattr_event(struct mrp_applicant *app, 64862306a36Sopenharmony_ci struct sk_buff *skb, 64962306a36Sopenharmony_ci enum mrp_vecattr_event vaevent) 65062306a36Sopenharmony_ci{ 65162306a36Sopenharmony_ci struct mrp_attr *attr; 65262306a36Sopenharmony_ci enum mrp_event event; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci attr = mrp_attr_lookup(app, mrp_cb(skb)->attrvalue, 65562306a36Sopenharmony_ci mrp_cb(skb)->mh->attrlen, 65662306a36Sopenharmony_ci mrp_cb(skb)->mh->attrtype); 65762306a36Sopenharmony_ci if (attr == NULL) 65862306a36Sopenharmony_ci return; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci switch (vaevent) { 66162306a36Sopenharmony_ci case MRP_VECATTR_EVENT_NEW: 66262306a36Sopenharmony_ci event = MRP_EVENT_R_NEW; 66362306a36Sopenharmony_ci break; 66462306a36Sopenharmony_ci case MRP_VECATTR_EVENT_JOIN_IN: 66562306a36Sopenharmony_ci event = MRP_EVENT_R_JOIN_IN; 66662306a36Sopenharmony_ci break; 66762306a36Sopenharmony_ci case MRP_VECATTR_EVENT_IN: 66862306a36Sopenharmony_ci event = MRP_EVENT_R_IN; 66962306a36Sopenharmony_ci break; 67062306a36Sopenharmony_ci case MRP_VECATTR_EVENT_JOIN_MT: 67162306a36Sopenharmony_ci event = MRP_EVENT_R_JOIN_MT; 67262306a36Sopenharmony_ci break; 67362306a36Sopenharmony_ci case MRP_VECATTR_EVENT_MT: 67462306a36Sopenharmony_ci event = MRP_EVENT_R_MT; 67562306a36Sopenharmony_ci break; 67662306a36Sopenharmony_ci case MRP_VECATTR_EVENT_LV: 67762306a36Sopenharmony_ci event = MRP_EVENT_R_LV; 67862306a36Sopenharmony_ci break; 67962306a36Sopenharmony_ci default: 68062306a36Sopenharmony_ci return; 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci mrp_attr_event(app, attr, event); 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_cistatic int mrp_pdu_parse_vecattr(struct mrp_applicant *app, 68762306a36Sopenharmony_ci struct sk_buff *skb, int *offset) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci struct mrp_vecattr_hdr _vah; 69062306a36Sopenharmony_ci u16 valen; 69162306a36Sopenharmony_ci u8 vaevents, vaevent; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci mrp_cb(skb)->vah = skb_header_pointer(skb, *offset, sizeof(_vah), 69462306a36Sopenharmony_ci &_vah); 69562306a36Sopenharmony_ci if (!mrp_cb(skb)->vah) 69662306a36Sopenharmony_ci return -1; 69762306a36Sopenharmony_ci *offset += sizeof(_vah); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci if (get_unaligned(&mrp_cb(skb)->vah->lenflags) & 70062306a36Sopenharmony_ci MRP_VECATTR_HDR_FLAG_LA) 70162306a36Sopenharmony_ci mrp_mad_event(app, MRP_EVENT_R_LA); 70262306a36Sopenharmony_ci valen = be16_to_cpu(get_unaligned(&mrp_cb(skb)->vah->lenflags) & 70362306a36Sopenharmony_ci MRP_VECATTR_HDR_LEN_MASK); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci /* The VectorAttribute structure in a PDU carries event information 70662306a36Sopenharmony_ci * about one or more attributes having consecutive values. Only the 70762306a36Sopenharmony_ci * value for the first attribute is contained in the structure. So 70862306a36Sopenharmony_ci * we make a copy of that value, and then increment it each time we 70962306a36Sopenharmony_ci * advance to the next event in its Vector. 71062306a36Sopenharmony_ci */ 71162306a36Sopenharmony_ci if (sizeof(struct mrp_skb_cb) + mrp_cb(skb)->mh->attrlen > 71262306a36Sopenharmony_ci sizeof_field(struct sk_buff, cb)) 71362306a36Sopenharmony_ci return -1; 71462306a36Sopenharmony_ci if (skb_copy_bits(skb, *offset, mrp_cb(skb)->attrvalue, 71562306a36Sopenharmony_ci mrp_cb(skb)->mh->attrlen) < 0) 71662306a36Sopenharmony_ci return -1; 71762306a36Sopenharmony_ci *offset += mrp_cb(skb)->mh->attrlen; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci /* In a VectorAttribute, the Vector contains events which are packed 72062306a36Sopenharmony_ci * three to a byte. We process one byte of the Vector at a time. 72162306a36Sopenharmony_ci */ 72262306a36Sopenharmony_ci while (valen > 0) { 72362306a36Sopenharmony_ci if (skb_copy_bits(skb, *offset, &vaevents, 72462306a36Sopenharmony_ci sizeof(vaevents)) < 0) 72562306a36Sopenharmony_ci return -1; 72662306a36Sopenharmony_ci *offset += sizeof(vaevents); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci /* Extract and process the first event. */ 72962306a36Sopenharmony_ci vaevent = vaevents / (__MRP_VECATTR_EVENT_MAX * 73062306a36Sopenharmony_ci __MRP_VECATTR_EVENT_MAX); 73162306a36Sopenharmony_ci if (vaevent >= __MRP_VECATTR_EVENT_MAX) { 73262306a36Sopenharmony_ci /* The byte is malformed; stop processing. */ 73362306a36Sopenharmony_ci return -1; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci mrp_pdu_parse_vecattr_event(app, skb, vaevent); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci /* If present, extract and process the second event. */ 73862306a36Sopenharmony_ci if (!--valen) 73962306a36Sopenharmony_ci break; 74062306a36Sopenharmony_ci mrp_attrvalue_inc(mrp_cb(skb)->attrvalue, 74162306a36Sopenharmony_ci mrp_cb(skb)->mh->attrlen); 74262306a36Sopenharmony_ci vaevents %= (__MRP_VECATTR_EVENT_MAX * 74362306a36Sopenharmony_ci __MRP_VECATTR_EVENT_MAX); 74462306a36Sopenharmony_ci vaevent = vaevents / __MRP_VECATTR_EVENT_MAX; 74562306a36Sopenharmony_ci mrp_pdu_parse_vecattr_event(app, skb, vaevent); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci /* If present, extract and process the third event. */ 74862306a36Sopenharmony_ci if (!--valen) 74962306a36Sopenharmony_ci break; 75062306a36Sopenharmony_ci mrp_attrvalue_inc(mrp_cb(skb)->attrvalue, 75162306a36Sopenharmony_ci mrp_cb(skb)->mh->attrlen); 75262306a36Sopenharmony_ci vaevents %= __MRP_VECATTR_EVENT_MAX; 75362306a36Sopenharmony_ci vaevent = vaevents; 75462306a36Sopenharmony_ci mrp_pdu_parse_vecattr_event(app, skb, vaevent); 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci return 0; 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_cistatic int mrp_pdu_parse_msg(struct mrp_applicant *app, struct sk_buff *skb, 76062306a36Sopenharmony_ci int *offset) 76162306a36Sopenharmony_ci{ 76262306a36Sopenharmony_ci struct mrp_msg_hdr _mh; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci mrp_cb(skb)->mh = skb_header_pointer(skb, *offset, sizeof(_mh), &_mh); 76562306a36Sopenharmony_ci if (!mrp_cb(skb)->mh) 76662306a36Sopenharmony_ci return -1; 76762306a36Sopenharmony_ci *offset += sizeof(_mh); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci if (mrp_cb(skb)->mh->attrtype == 0 || 77062306a36Sopenharmony_ci mrp_cb(skb)->mh->attrtype > app->app->maxattr || 77162306a36Sopenharmony_ci mrp_cb(skb)->mh->attrlen == 0) 77262306a36Sopenharmony_ci return -1; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci while (skb->len > *offset) { 77562306a36Sopenharmony_ci if (mrp_pdu_parse_end_mark(skb, offset) < 0) 77662306a36Sopenharmony_ci break; 77762306a36Sopenharmony_ci if (mrp_pdu_parse_vecattr(app, skb, offset) < 0) 77862306a36Sopenharmony_ci return -1; 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci return 0; 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic int mrp_rcv(struct sk_buff *skb, struct net_device *dev, 78462306a36Sopenharmony_ci struct packet_type *pt, struct net_device *orig_dev) 78562306a36Sopenharmony_ci{ 78662306a36Sopenharmony_ci struct mrp_application *appl = container_of(pt, struct mrp_application, 78762306a36Sopenharmony_ci pkttype); 78862306a36Sopenharmony_ci struct mrp_port *port; 78962306a36Sopenharmony_ci struct mrp_applicant *app; 79062306a36Sopenharmony_ci struct mrp_pdu_hdr _ph; 79162306a36Sopenharmony_ci const struct mrp_pdu_hdr *ph; 79262306a36Sopenharmony_ci int offset = skb_network_offset(skb); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci /* If the interface is in promiscuous mode, drop the packet if 79562306a36Sopenharmony_ci * it was unicast to another host. 79662306a36Sopenharmony_ci */ 79762306a36Sopenharmony_ci if (unlikely(skb->pkt_type == PACKET_OTHERHOST)) 79862306a36Sopenharmony_ci goto out; 79962306a36Sopenharmony_ci skb = skb_share_check(skb, GFP_ATOMIC); 80062306a36Sopenharmony_ci if (unlikely(!skb)) 80162306a36Sopenharmony_ci goto out; 80262306a36Sopenharmony_ci port = rcu_dereference(dev->mrp_port); 80362306a36Sopenharmony_ci if (unlikely(!port)) 80462306a36Sopenharmony_ci goto out; 80562306a36Sopenharmony_ci app = rcu_dereference(port->applicants[appl->type]); 80662306a36Sopenharmony_ci if (unlikely(!app)) 80762306a36Sopenharmony_ci goto out; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci ph = skb_header_pointer(skb, offset, sizeof(_ph), &_ph); 81062306a36Sopenharmony_ci if (!ph) 81162306a36Sopenharmony_ci goto out; 81262306a36Sopenharmony_ci offset += sizeof(_ph); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci if (ph->version != app->app->version) 81562306a36Sopenharmony_ci goto out; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci spin_lock(&app->lock); 81862306a36Sopenharmony_ci while (skb->len > offset) { 81962306a36Sopenharmony_ci if (mrp_pdu_parse_end_mark(skb, &offset) < 0) 82062306a36Sopenharmony_ci break; 82162306a36Sopenharmony_ci if (mrp_pdu_parse_msg(app, skb, &offset) < 0) 82262306a36Sopenharmony_ci break; 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci spin_unlock(&app->lock); 82562306a36Sopenharmony_ciout: 82662306a36Sopenharmony_ci kfree_skb(skb); 82762306a36Sopenharmony_ci return 0; 82862306a36Sopenharmony_ci} 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_cistatic int mrp_init_port(struct net_device *dev) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci struct mrp_port *port; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci port = kzalloc(sizeof(*port), GFP_KERNEL); 83562306a36Sopenharmony_ci if (!port) 83662306a36Sopenharmony_ci return -ENOMEM; 83762306a36Sopenharmony_ci rcu_assign_pointer(dev->mrp_port, port); 83862306a36Sopenharmony_ci return 0; 83962306a36Sopenharmony_ci} 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_cistatic void mrp_release_port(struct net_device *dev) 84262306a36Sopenharmony_ci{ 84362306a36Sopenharmony_ci struct mrp_port *port = rtnl_dereference(dev->mrp_port); 84462306a36Sopenharmony_ci unsigned int i; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci for (i = 0; i <= MRP_APPLICATION_MAX; i++) { 84762306a36Sopenharmony_ci if (rtnl_dereference(port->applicants[i])) 84862306a36Sopenharmony_ci return; 84962306a36Sopenharmony_ci } 85062306a36Sopenharmony_ci RCU_INIT_POINTER(dev->mrp_port, NULL); 85162306a36Sopenharmony_ci kfree_rcu(port, rcu); 85262306a36Sopenharmony_ci} 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ciint mrp_init_applicant(struct net_device *dev, struct mrp_application *appl) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci struct mrp_applicant *app; 85762306a36Sopenharmony_ci int err; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci ASSERT_RTNL(); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci if (!rtnl_dereference(dev->mrp_port)) { 86262306a36Sopenharmony_ci err = mrp_init_port(dev); 86362306a36Sopenharmony_ci if (err < 0) 86462306a36Sopenharmony_ci goto err1; 86562306a36Sopenharmony_ci } 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci err = -ENOMEM; 86862306a36Sopenharmony_ci app = kzalloc(sizeof(*app), GFP_KERNEL); 86962306a36Sopenharmony_ci if (!app) 87062306a36Sopenharmony_ci goto err2; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci err = dev_mc_add(dev, appl->group_address); 87362306a36Sopenharmony_ci if (err < 0) 87462306a36Sopenharmony_ci goto err3; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci app->dev = dev; 87762306a36Sopenharmony_ci app->app = appl; 87862306a36Sopenharmony_ci app->mad = RB_ROOT; 87962306a36Sopenharmony_ci app->active = true; 88062306a36Sopenharmony_ci spin_lock_init(&app->lock); 88162306a36Sopenharmony_ci skb_queue_head_init(&app->queue); 88262306a36Sopenharmony_ci rcu_assign_pointer(dev->mrp_port->applicants[appl->type], app); 88362306a36Sopenharmony_ci timer_setup(&app->join_timer, mrp_join_timer, 0); 88462306a36Sopenharmony_ci mrp_join_timer_arm(app); 88562306a36Sopenharmony_ci timer_setup(&app->periodic_timer, mrp_periodic_timer, 0); 88662306a36Sopenharmony_ci mrp_periodic_timer_arm(app); 88762306a36Sopenharmony_ci return 0; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_cierr3: 89062306a36Sopenharmony_ci kfree(app); 89162306a36Sopenharmony_cierr2: 89262306a36Sopenharmony_ci mrp_release_port(dev); 89362306a36Sopenharmony_cierr1: 89462306a36Sopenharmony_ci return err; 89562306a36Sopenharmony_ci} 89662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mrp_init_applicant); 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_civoid mrp_uninit_applicant(struct net_device *dev, struct mrp_application *appl) 89962306a36Sopenharmony_ci{ 90062306a36Sopenharmony_ci struct mrp_port *port = rtnl_dereference(dev->mrp_port); 90162306a36Sopenharmony_ci struct mrp_applicant *app = rtnl_dereference( 90262306a36Sopenharmony_ci port->applicants[appl->type]); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci ASSERT_RTNL(); 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci RCU_INIT_POINTER(port->applicants[appl->type], NULL); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci spin_lock_bh(&app->lock); 90962306a36Sopenharmony_ci app->active = false; 91062306a36Sopenharmony_ci spin_unlock_bh(&app->lock); 91162306a36Sopenharmony_ci /* Delete timer and generate a final TX event to flush out 91262306a36Sopenharmony_ci * all pending messages before the applicant is gone. 91362306a36Sopenharmony_ci */ 91462306a36Sopenharmony_ci timer_shutdown_sync(&app->join_timer); 91562306a36Sopenharmony_ci timer_shutdown_sync(&app->periodic_timer); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci spin_lock_bh(&app->lock); 91862306a36Sopenharmony_ci mrp_mad_event(app, MRP_EVENT_TX); 91962306a36Sopenharmony_ci mrp_attr_destroy_all(app); 92062306a36Sopenharmony_ci mrp_pdu_queue(app); 92162306a36Sopenharmony_ci spin_unlock_bh(&app->lock); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci mrp_queue_xmit(app); 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci dev_mc_del(dev, appl->group_address); 92662306a36Sopenharmony_ci kfree_rcu(app, rcu); 92762306a36Sopenharmony_ci mrp_release_port(dev); 92862306a36Sopenharmony_ci} 92962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mrp_uninit_applicant); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ciint mrp_register_application(struct mrp_application *appl) 93262306a36Sopenharmony_ci{ 93362306a36Sopenharmony_ci appl->pkttype.func = mrp_rcv; 93462306a36Sopenharmony_ci dev_add_pack(&appl->pkttype); 93562306a36Sopenharmony_ci return 0; 93662306a36Sopenharmony_ci} 93762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mrp_register_application); 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_civoid mrp_unregister_application(struct mrp_application *appl) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci dev_remove_pack(&appl->pkttype); 94262306a36Sopenharmony_ci} 94362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mrp_unregister_application); 944