root/net/bridge/br_stp_bpdu.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. br_send_bpdu
  2. br_set_ticks
  3. br_get_ticks
  4. br_send_config_bpdu
  5. br_send_tcn_bpdu
  6. br_stp_rcv

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  *      Spanning tree protocol; BPDU handling
   4  *      Linux ethernet bridge
   5  *
   6  *      Authors:
   7  *      Lennert Buytenhek               <buytenh@gnu.org>
   8  */
   9 
  10 #include <linux/kernel.h>
  11 #include <linux/netfilter_bridge.h>
  12 #include <linux/etherdevice.h>
  13 #include <linux/llc.h>
  14 #include <linux/slab.h>
  15 #include <linux/pkt_sched.h>
  16 #include <net/net_namespace.h>
  17 #include <net/llc.h>
  18 #include <net/llc_pdu.h>
  19 #include <net/stp.h>
  20 #include <asm/unaligned.h>
  21 
  22 #include "br_private.h"
  23 #include "br_private_stp.h"
  24 
  25 #define STP_HZ          256
  26 
  27 #define LLC_RESERVE sizeof(struct llc_pdu_un)
  28 
  29 static int br_send_bpdu_finish(struct net *net, struct sock *sk,
  30                                struct sk_buff *skb)
  31 {
  32         return dev_queue_xmit(skb);
  33 }
  34 
  35 static void br_send_bpdu(struct net_bridge_port *p,
  36                          const unsigned char *data, int length)
  37 {
  38         struct sk_buff *skb;
  39 
  40         skb = dev_alloc_skb(length+LLC_RESERVE);
  41         if (!skb)
  42                 return;
  43 
  44         skb->dev = p->dev;
  45         skb->protocol = htons(ETH_P_802_2);
  46         skb->priority = TC_PRIO_CONTROL;
  47 
  48         skb_reserve(skb, LLC_RESERVE);
  49         __skb_put_data(skb, data, length);
  50 
  51         llc_pdu_header_init(skb, LLC_PDU_TYPE_U, LLC_SAP_BSPAN,
  52                             LLC_SAP_BSPAN, LLC_PDU_CMD);
  53         llc_pdu_init_as_ui_cmd(skb);
  54 
  55         llc_mac_hdr_init(skb, p->dev->dev_addr, p->br->group_addr);
  56 
  57         skb_reset_mac_header(skb);
  58 
  59         NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT,
  60                 dev_net(p->dev), NULL, skb, NULL, skb->dev,
  61                 br_send_bpdu_finish);
  62 }
  63 
  64 static inline void br_set_ticks(unsigned char *dest, int j)
  65 {
  66         unsigned long ticks = (STP_HZ * j)/ HZ;
  67 
  68         put_unaligned_be16(ticks, dest);
  69 }
  70 
  71 static inline int br_get_ticks(const unsigned char *src)
  72 {
  73         unsigned long ticks = get_unaligned_be16(src);
  74 
  75         return DIV_ROUND_UP(ticks * HZ, STP_HZ);
  76 }
  77 
  78 /* called under bridge lock */
  79 void br_send_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu)
  80 {
  81         unsigned char buf[35];
  82 
  83         if (p->br->stp_enabled != BR_KERNEL_STP)
  84                 return;
  85 
  86         buf[0] = 0;
  87         buf[1] = 0;
  88         buf[2] = 0;
  89         buf[3] = BPDU_TYPE_CONFIG;
  90         buf[4] = (bpdu->topology_change ? 0x01 : 0) |
  91                 (bpdu->topology_change_ack ? 0x80 : 0);
  92         buf[5] = bpdu->root.prio[0];
  93         buf[6] = bpdu->root.prio[1];
  94         buf[7] = bpdu->root.addr[0];
  95         buf[8] = bpdu->root.addr[1];
  96         buf[9] = bpdu->root.addr[2];
  97         buf[10] = bpdu->root.addr[3];
  98         buf[11] = bpdu->root.addr[4];
  99         buf[12] = bpdu->root.addr[5];
 100         buf[13] = (bpdu->root_path_cost >> 24) & 0xFF;
 101         buf[14] = (bpdu->root_path_cost >> 16) & 0xFF;
 102         buf[15] = (bpdu->root_path_cost >> 8) & 0xFF;
 103         buf[16] = bpdu->root_path_cost & 0xFF;
 104         buf[17] = bpdu->bridge_id.prio[0];
 105         buf[18] = bpdu->bridge_id.prio[1];
 106         buf[19] = bpdu->bridge_id.addr[0];
 107         buf[20] = bpdu->bridge_id.addr[1];
 108         buf[21] = bpdu->bridge_id.addr[2];
 109         buf[22] = bpdu->bridge_id.addr[3];
 110         buf[23] = bpdu->bridge_id.addr[4];
 111         buf[24] = bpdu->bridge_id.addr[5];
 112         buf[25] = (bpdu->port_id >> 8) & 0xFF;
 113         buf[26] = bpdu->port_id & 0xFF;
 114 
 115         br_set_ticks(buf+27, bpdu->message_age);
 116         br_set_ticks(buf+29, bpdu->max_age);
 117         br_set_ticks(buf+31, bpdu->hello_time);
 118         br_set_ticks(buf+33, bpdu->forward_delay);
 119 
 120         br_send_bpdu(p, buf, 35);
 121 }
 122 
 123 /* called under bridge lock */
 124 void br_send_tcn_bpdu(struct net_bridge_port *p)
 125 {
 126         unsigned char buf[4];
 127 
 128         if (p->br->stp_enabled != BR_KERNEL_STP)
 129                 return;
 130 
 131         buf[0] = 0;
 132         buf[1] = 0;
 133         buf[2] = 0;
 134         buf[3] = BPDU_TYPE_TCN;
 135         br_send_bpdu(p, buf, 4);
 136 }
 137 
 138 /*
 139  * Called from llc.
 140  *
 141  * NO locks, but rcu_read_lock
 142  */
 143 void br_stp_rcv(const struct stp_proto *proto, struct sk_buff *skb,
 144                 struct net_device *dev)
 145 {
 146         struct net_bridge_port *p;
 147         struct net_bridge *br;
 148         const unsigned char *buf;
 149 
 150         if (!pskb_may_pull(skb, 4))
 151                 goto err;
 152 
 153         /* compare of protocol id and version */
 154         buf = skb->data;
 155         if (buf[0] != 0 || buf[1] != 0 || buf[2] != 0)
 156                 goto err;
 157 
 158         p = br_port_get_check_rcu(dev);
 159         if (!p)
 160                 goto err;
 161 
 162         br = p->br;
 163         spin_lock(&br->lock);
 164 
 165         if (br->stp_enabled != BR_KERNEL_STP)
 166                 goto out;
 167 
 168         if (!(br->dev->flags & IFF_UP))
 169                 goto out;
 170 
 171         if (p->state == BR_STATE_DISABLED)
 172                 goto out;
 173 
 174         if (!ether_addr_equal(eth_hdr(skb)->h_dest, br->group_addr))
 175                 goto out;
 176 
 177         if (p->flags & BR_BPDU_GUARD) {
 178                 br_notice(br, "BPDU received on blocked port %u(%s)\n",
 179                           (unsigned int) p->port_no, p->dev->name);
 180                 br_stp_disable_port(p);
 181                 goto out;
 182         }
 183 
 184         buf = skb_pull(skb, 3);
 185 
 186         if (buf[0] == BPDU_TYPE_CONFIG) {
 187                 struct br_config_bpdu bpdu;
 188 
 189                 if (!pskb_may_pull(skb, 32))
 190                         goto out;
 191 
 192                 buf = skb->data;
 193                 bpdu.topology_change = (buf[1] & 0x01) ? 1 : 0;
 194                 bpdu.topology_change_ack = (buf[1] & 0x80) ? 1 : 0;
 195 
 196                 bpdu.root.prio[0] = buf[2];
 197                 bpdu.root.prio[1] = buf[3];
 198                 bpdu.root.addr[0] = buf[4];
 199                 bpdu.root.addr[1] = buf[5];
 200                 bpdu.root.addr[2] = buf[6];
 201                 bpdu.root.addr[3] = buf[7];
 202                 bpdu.root.addr[4] = buf[8];
 203                 bpdu.root.addr[5] = buf[9];
 204                 bpdu.root_path_cost =
 205                         (buf[10] << 24) |
 206                         (buf[11] << 16) |
 207                         (buf[12] << 8) |
 208                         buf[13];
 209                 bpdu.bridge_id.prio[0] = buf[14];
 210                 bpdu.bridge_id.prio[1] = buf[15];
 211                 bpdu.bridge_id.addr[0] = buf[16];
 212                 bpdu.bridge_id.addr[1] = buf[17];
 213                 bpdu.bridge_id.addr[2] = buf[18];
 214                 bpdu.bridge_id.addr[3] = buf[19];
 215                 bpdu.bridge_id.addr[4] = buf[20];
 216                 bpdu.bridge_id.addr[5] = buf[21];
 217                 bpdu.port_id = (buf[22] << 8) | buf[23];
 218 
 219                 bpdu.message_age = br_get_ticks(buf+24);
 220                 bpdu.max_age = br_get_ticks(buf+26);
 221                 bpdu.hello_time = br_get_ticks(buf+28);
 222                 bpdu.forward_delay = br_get_ticks(buf+30);
 223 
 224                 if (bpdu.message_age > bpdu.max_age) {
 225                         if (net_ratelimit())
 226                                 br_notice(p->br,
 227                                           "port %u config from %pM"
 228                                           " (message_age %ul > max_age %ul)\n",
 229                                           p->port_no,
 230                                           eth_hdr(skb)->h_source,
 231                                           bpdu.message_age, bpdu.max_age);
 232                         goto out;
 233                 }
 234 
 235                 br_received_config_bpdu(p, &bpdu);
 236         } else if (buf[0] == BPDU_TYPE_TCN) {
 237                 br_received_tcn_bpdu(p);
 238         }
 239  out:
 240         spin_unlock(&br->lock);
 241  err:
 242         kfree_skb(skb);
 243 }

/* [<][>][^][v][top][bottom][index][help] */