br_vlan_options.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. // Copyright (c) 2020, Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
  3. #include <linux/kernel.h>
  4. #include <linux/netdevice.h>
  5. #include <linux/rtnetlink.h>
  6. #include <linux/slab.h>
  7. #include <net/ip_tunnels.h>
  8. #include "br_private.h"
  9. #include "br_private_tunnel.h"
  10. static bool __vlan_tun_put(struct sk_buff *skb, const struct net_bridge_vlan *v)
  11. {
  12. __be32 tid = tunnel_id_to_key32(v->tinfo.tunnel_id);
  13. struct nlattr *nest;
  14. if (!v->tinfo.tunnel_dst)
  15. return true;
  16. nest = nla_nest_start(skb, BRIDGE_VLANDB_ENTRY_TUNNEL_INFO);
  17. if (!nest)
  18. return false;
  19. if (nla_put_u32(skb, BRIDGE_VLANDB_TINFO_ID, be32_to_cpu(tid))) {
  20. nla_nest_cancel(skb, nest);
  21. return false;
  22. }
  23. nla_nest_end(skb, nest);
  24. return true;
  25. }
  26. static bool __vlan_tun_can_enter_range(const struct net_bridge_vlan *v_curr,
  27. const struct net_bridge_vlan *range_end)
  28. {
  29. return (!v_curr->tinfo.tunnel_dst && !range_end->tinfo.tunnel_dst) ||
  30. vlan_tunid_inrange(v_curr, range_end);
  31. }
  32. /* check if the options' state of v_curr allow it to enter the range */
  33. bool br_vlan_opts_eq_range(const struct net_bridge_vlan *v_curr,
  34. const struct net_bridge_vlan *range_end)
  35. {
  36. return v_curr->state == range_end->state &&
  37. __vlan_tun_can_enter_range(v_curr, range_end);
  38. }
  39. bool br_vlan_opts_fill(struct sk_buff *skb, const struct net_bridge_vlan *v)
  40. {
  41. return !nla_put_u8(skb, BRIDGE_VLANDB_ENTRY_STATE,
  42. br_vlan_get_state(v)) &&
  43. __vlan_tun_put(skb, v);
  44. }
  45. size_t br_vlan_opts_nl_size(void)
  46. {
  47. return nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_ENTRY_STATE */
  48. + nla_total_size(0) /* BRIDGE_VLANDB_ENTRY_TUNNEL_INFO */
  49. + nla_total_size(sizeof(u32)); /* BRIDGE_VLANDB_TINFO_ID */
  50. }
  51. static int br_vlan_modify_state(struct net_bridge_vlan_group *vg,
  52. struct net_bridge_vlan *v,
  53. u8 state,
  54. bool *changed,
  55. struct netlink_ext_ack *extack)
  56. {
  57. struct net_bridge *br;
  58. ASSERT_RTNL();
  59. if (state > BR_STATE_BLOCKING) {
  60. NL_SET_ERR_MSG_MOD(extack, "Invalid vlan state");
  61. return -EINVAL;
  62. }
  63. if (br_vlan_is_brentry(v))
  64. br = v->br;
  65. else
  66. br = v->port->br;
  67. if (br->stp_enabled == BR_KERNEL_STP) {
  68. NL_SET_ERR_MSG_MOD(extack, "Can't modify vlan state when using kernel STP");
  69. return -EBUSY;
  70. }
  71. if (v->state == state)
  72. return 0;
  73. if (v->vid == br_get_pvid(vg))
  74. br_vlan_set_pvid_state(vg, state);
  75. br_vlan_set_state(v, state);
  76. *changed = true;
  77. return 0;
  78. }
  79. static const struct nla_policy br_vlandb_tinfo_pol[BRIDGE_VLANDB_TINFO_MAX + 1] = {
  80. [BRIDGE_VLANDB_TINFO_ID] = { .type = NLA_U32 },
  81. [BRIDGE_VLANDB_TINFO_CMD] = { .type = NLA_U32 },
  82. };
  83. static int br_vlan_modify_tunnel(const struct net_bridge_port *p,
  84. struct net_bridge_vlan *v,
  85. struct nlattr **tb,
  86. bool *changed,
  87. struct netlink_ext_ack *extack)
  88. {
  89. struct nlattr *tun_tb[BRIDGE_VLANDB_TINFO_MAX + 1], *attr;
  90. struct bridge_vlan_info *vinfo;
  91. u32 tun_id = 0;
  92. int cmd, err;
  93. if (!p) {
  94. NL_SET_ERR_MSG_MOD(extack, "Can't modify tunnel mapping of non-port vlans");
  95. return -EINVAL;
  96. }
  97. if (!(p->flags & BR_VLAN_TUNNEL)) {
  98. NL_SET_ERR_MSG_MOD(extack, "Port doesn't have tunnel flag set");
  99. return -EINVAL;
  100. }
  101. attr = tb[BRIDGE_VLANDB_ENTRY_TUNNEL_INFO];
  102. err = nla_parse_nested(tun_tb, BRIDGE_VLANDB_TINFO_MAX, attr,
  103. br_vlandb_tinfo_pol, extack);
  104. if (err)
  105. return err;
  106. if (!tun_tb[BRIDGE_VLANDB_TINFO_CMD]) {
  107. NL_SET_ERR_MSG_MOD(extack, "Missing tunnel command attribute");
  108. return -ENOENT;
  109. }
  110. cmd = nla_get_u32(tun_tb[BRIDGE_VLANDB_TINFO_CMD]);
  111. switch (cmd) {
  112. case RTM_SETLINK:
  113. if (!tun_tb[BRIDGE_VLANDB_TINFO_ID]) {
  114. NL_SET_ERR_MSG_MOD(extack, "Missing tunnel id attribute");
  115. return -ENOENT;
  116. }
  117. /* when working on vlan ranges this is the starting tunnel id */
  118. tun_id = nla_get_u32(tun_tb[BRIDGE_VLANDB_TINFO_ID]);
  119. /* vlan info attr is guaranteed by br_vlan_rtm_process_one */
  120. vinfo = nla_data(tb[BRIDGE_VLANDB_ENTRY_INFO]);
  121. /* tunnel ids are mapped to each vlan in increasing order,
  122. * the starting vlan is in BRIDGE_VLANDB_ENTRY_INFO and v is the
  123. * current vlan, so we compute: tun_id + v - vinfo->vid
  124. */
  125. tun_id += v->vid - vinfo->vid;
  126. break;
  127. case RTM_DELLINK:
  128. break;
  129. default:
  130. NL_SET_ERR_MSG_MOD(extack, "Unsupported tunnel command");
  131. return -EINVAL;
  132. }
  133. return br_vlan_tunnel_info(p, cmd, v->vid, tun_id, changed);
  134. }
  135. static int br_vlan_process_one_opts(const struct net_bridge *br,
  136. const struct net_bridge_port *p,
  137. struct net_bridge_vlan_group *vg,
  138. struct net_bridge_vlan *v,
  139. struct nlattr **tb,
  140. bool *changed,
  141. struct netlink_ext_ack *extack)
  142. {
  143. int err;
  144. *changed = false;
  145. if (tb[BRIDGE_VLANDB_ENTRY_STATE]) {
  146. u8 state = nla_get_u8(tb[BRIDGE_VLANDB_ENTRY_STATE]);
  147. err = br_vlan_modify_state(vg, v, state, changed, extack);
  148. if (err)
  149. return err;
  150. }
  151. if (tb[BRIDGE_VLANDB_ENTRY_TUNNEL_INFO]) {
  152. err = br_vlan_modify_tunnel(p, v, tb, changed, extack);
  153. if (err)
  154. return err;
  155. }
  156. return 0;
  157. }
  158. int br_vlan_process_options(const struct net_bridge *br,
  159. const struct net_bridge_port *p,
  160. struct net_bridge_vlan *range_start,
  161. struct net_bridge_vlan *range_end,
  162. struct nlattr **tb,
  163. struct netlink_ext_ack *extack)
  164. {
  165. struct net_bridge_vlan *v, *curr_start = NULL, *curr_end = NULL;
  166. struct net_bridge_vlan_group *vg;
  167. int vid, err = 0;
  168. u16 pvid;
  169. if (p)
  170. vg = nbp_vlan_group(p);
  171. else
  172. vg = br_vlan_group(br);
  173. if (!range_start || !br_vlan_should_use(range_start)) {
  174. NL_SET_ERR_MSG_MOD(extack, "Vlan range start doesn't exist, can't process options");
  175. return -ENOENT;
  176. }
  177. if (!range_end || !br_vlan_should_use(range_end)) {
  178. NL_SET_ERR_MSG_MOD(extack, "Vlan range end doesn't exist, can't process options");
  179. return -ENOENT;
  180. }
  181. pvid = br_get_pvid(vg);
  182. for (vid = range_start->vid; vid <= range_end->vid; vid++) {
  183. bool changed = false;
  184. v = br_vlan_find(vg, vid);
  185. if (!v || !br_vlan_should_use(v)) {
  186. NL_SET_ERR_MSG_MOD(extack, "Vlan in range doesn't exist, can't process options");
  187. err = -ENOENT;
  188. break;
  189. }
  190. err = br_vlan_process_one_opts(br, p, vg, v, tb, &changed,
  191. extack);
  192. if (err)
  193. break;
  194. if (changed) {
  195. /* vlan options changed, check for range */
  196. if (!curr_start) {
  197. curr_start = v;
  198. curr_end = v;
  199. continue;
  200. }
  201. if (v->vid == pvid ||
  202. !br_vlan_can_enter_range(v, curr_end)) {
  203. br_vlan_notify(br, p, curr_start->vid,
  204. curr_end->vid, RTM_NEWVLAN);
  205. curr_start = v;
  206. }
  207. curr_end = v;
  208. } else {
  209. /* nothing changed and nothing to notify yet */
  210. if (!curr_start)
  211. continue;
  212. br_vlan_notify(br, p, curr_start->vid, curr_end->vid,
  213. RTM_NEWVLAN);
  214. curr_start = NULL;
  215. curr_end = NULL;
  216. }
  217. }
  218. if (curr_start)
  219. br_vlan_notify(br, p, curr_start->vid, curr_end->vid,
  220. RTM_NEWVLAN);
  221. return err;
  222. }