1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include <linux/ethtool_netlink.h> 4 #include <net/udp_tunnel.h> 5 #include <net/vxlan.h> 6 7 #include "bitset.h" 8 #include "common.h" 9 #include "netlink.h" 10 11 const struct nla_policy ethnl_tunnel_info_get_policy[] = { 12 [ETHTOOL_A_TUNNEL_INFO_HEADER] = 13 NLA_POLICY_NESTED(ethnl_header_policy), 14 }; 15 16 static_assert(ETHTOOL_UDP_TUNNEL_TYPE_VXLAN == ilog2(UDP_TUNNEL_TYPE_VXLAN)); 17 static_assert(ETHTOOL_UDP_TUNNEL_TYPE_GENEVE == ilog2(UDP_TUNNEL_TYPE_GENEVE)); 18 static_assert(ETHTOOL_UDP_TUNNEL_TYPE_VXLAN_GPE == 19 ilog2(UDP_TUNNEL_TYPE_VXLAN_GPE)); 20 21 static ssize_t ethnl_udp_table_reply_size(unsigned int types, bool compact) 22 { 23 ssize_t size; 24 25 size = ethnl_bitset32_size(&types, NULL, __ETHTOOL_UDP_TUNNEL_TYPE_CNT, 26 udp_tunnel_type_names, compact); 27 if (size < 0) 28 return size; 29 30 return size + 31 nla_total_size(0) + /* _UDP_TABLE */ 32 nla_total_size(sizeof(u32)); /* _UDP_TABLE_SIZE */ 33 } 34 35 static ssize_t 36 ethnl_tunnel_info_reply_size(const struct ethnl_req_info *req_base, 37 struct netlink_ext_ack *extack) 38 { 39 bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS; 40 const struct udp_tunnel_nic_info *info; 41 unsigned int i; 42 ssize_t ret; 43 size_t size; 44 45 info = req_base->dev->udp_tunnel_nic_info; 46 if (!info) { 47 NL_SET_ERR_MSG(extack, 48 "device does not report tunnel offload info"); 49 return -EOPNOTSUPP; 50 } 51 52 size = nla_total_size(0); /* _INFO_UDP_PORTS */ 53 54 for (i = 0; i < UDP_TUNNEL_NIC_MAX_TABLES; i++) { 55 if (!info->tables[i].n_entries) 56 break; 57 58 ret = ethnl_udp_table_reply_size(info->tables[i].tunnel_types, 59 compact); 60 if (ret < 0) 61 return ret; 62 size += ret; 63 64 size += udp_tunnel_nic_dump_size(req_base->dev, i); 65 } 66 67 if (info->flags & UDP_TUNNEL_NIC_INFO_STATIC_IANA_VXLAN) { 68 ret = ethnl_udp_table_reply_size(0, compact); 69 if (ret < 0) 70 return ret; 71 size += ret; 72 73 size += nla_total_size(0) + /* _TABLE_ENTRY */ 74 nla_total_size(sizeof(__be16)) + /* _ENTRY_PORT */ 75 nla_total_size(sizeof(u32)); /* _ENTRY_TYPE */ 76 } 77 78 return size; 79 } 80 81 static int 82 ethnl_tunnel_info_fill_reply(const struct ethnl_req_info *req_base, 83 struct sk_buff *skb) 84 { 85 bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS; 86 const struct udp_tunnel_nic_info *info; 87 struct nlattr *ports, *table, *entry; 88 unsigned int i; 89 90 info = req_base->dev->udp_tunnel_nic_info; 91 if (!info) 92 return -EOPNOTSUPP; 93 94 ports = nla_nest_start(skb, ETHTOOL_A_TUNNEL_INFO_UDP_PORTS); 95 if (!ports) 96 return -EMSGSIZE; 97 98 for (i = 0; i < UDP_TUNNEL_NIC_MAX_TABLES; i++) { 99 if (!info->tables[i].n_entries) 100 break; 101 102 table = nla_nest_start(skb, ETHTOOL_A_TUNNEL_UDP_TABLE); 103 if (!table) 104 goto err_cancel_ports; 105 106 if (nla_put_u32(skb, ETHTOOL_A_TUNNEL_UDP_TABLE_SIZE, 107 info->tables[i].n_entries)) 108 goto err_cancel_table; 109 110 if (ethnl_put_bitset32(skb, ETHTOOL_A_TUNNEL_UDP_TABLE_TYPES, 111 &info->tables[i].tunnel_types, NULL, 112 __ETHTOOL_UDP_TUNNEL_TYPE_CNT, 113 udp_tunnel_type_names, compact)) 114 goto err_cancel_table; 115 116 if (udp_tunnel_nic_dump_write(req_base->dev, i, skb)) 117 goto err_cancel_table; 118 119 nla_nest_end(skb, table); 120 } 121 122 if (info->flags & UDP_TUNNEL_NIC_INFO_STATIC_IANA_VXLAN) { 123 u32 zero = 0; 124 125 table = nla_nest_start(skb, ETHTOOL_A_TUNNEL_UDP_TABLE); 126 if (!table) 127 goto err_cancel_ports; 128 129 if (nla_put_u32(skb, ETHTOOL_A_TUNNEL_UDP_TABLE_SIZE, 1)) 130 goto err_cancel_table; 131 132 if (ethnl_put_bitset32(skb, ETHTOOL_A_TUNNEL_UDP_TABLE_TYPES, 133 &zero, NULL, 134 __ETHTOOL_UDP_TUNNEL_TYPE_CNT, 135 udp_tunnel_type_names, compact)) 136 goto err_cancel_table; 137 138 entry = nla_nest_start(skb, ETHTOOL_A_TUNNEL_UDP_TABLE_ENTRY); 139 if (!entry) 140 goto err_cancel_entry; 141 142 if (nla_put_be16(skb, ETHTOOL_A_TUNNEL_UDP_ENTRY_PORT, 143 htons(IANA_VXLAN_UDP_PORT)) || 144 nla_put_u32(skb, ETHTOOL_A_TUNNEL_UDP_ENTRY_TYPE, 145 ilog2(UDP_TUNNEL_TYPE_VXLAN))) 146 goto err_cancel_entry; 147 148 nla_nest_end(skb, entry); 149 nla_nest_end(skb, table); 150 } 151 152 nla_nest_end(skb, ports); 153 154 return 0; 155 156 err_cancel_entry: 157 nla_nest_cancel(skb, entry); 158 err_cancel_table: 159 nla_nest_cancel(skb, table); 160 err_cancel_ports: 161 nla_nest_cancel(skb, ports); 162 return -EMSGSIZE; 163 } 164 165 int ethnl_tunnel_info_doit(struct sk_buff *skb, struct genl_info *info) 166 { 167 struct ethnl_req_info req_info = {}; 168 struct nlattr **tb = info->attrs; 169 struct sk_buff *rskb; 170 void *reply_payload; 171 int reply_len; 172 int ret; 173 174 ret = ethnl_parse_header_dev_get(&req_info, 175 tb[ETHTOOL_A_TUNNEL_INFO_HEADER], 176 genl_info_net(info), info->extack, 177 true); 178 if (ret < 0) 179 return ret; 180 181 rtnl_lock(); 182 ret = ethnl_tunnel_info_reply_size(&req_info, info->extack); 183 if (ret < 0) 184 goto err_unlock_rtnl; 185 reply_len = ret + ethnl_reply_header_size(); 186 187 rskb = ethnl_reply_init(reply_len, req_info.dev, 188 ETHTOOL_MSG_TUNNEL_INFO_GET_REPLY, 189 ETHTOOL_A_TUNNEL_INFO_HEADER, 190 info, &reply_payload); 191 if (!rskb) { 192 ret = -ENOMEM; 193 goto err_unlock_rtnl; 194 } 195 196 ret = ethnl_tunnel_info_fill_reply(&req_info, rskb); 197 if (ret) 198 goto err_free_msg; 199 rtnl_unlock(); 200 ethnl_parse_header_dev_put(&req_info); 201 genlmsg_end(rskb, reply_payload); 202 203 return genlmsg_reply(rskb, info); 204 205 err_free_msg: 206 nlmsg_free(rskb); 207 err_unlock_rtnl: 208 rtnl_unlock(); 209 ethnl_parse_header_dev_put(&req_info); 210 return ret; 211 } 212 213 struct ethnl_tunnel_info_dump_ctx { 214 struct ethnl_req_info req_info; 215 int pos_hash; 216 int pos_idx; 217 }; 218 219 int ethnl_tunnel_info_start(struct netlink_callback *cb) 220 { 221 const struct genl_dumpit_info *info = genl_dumpit_info(cb); 222 struct ethnl_tunnel_info_dump_ctx *ctx = (void *)cb->ctx; 223 struct nlattr **tb = info->attrs; 224 int ret; 225 226 BUILD_BUG_ON(sizeof(*ctx) > sizeof(cb->ctx)); 227 228 memset(ctx, 0, sizeof(*ctx)); 229 230 ret = ethnl_parse_header_dev_get(&ctx->req_info, 231 tb[ETHTOOL_A_TUNNEL_INFO_HEADER], 232 sock_net(cb->skb->sk), cb->extack, 233 false); 234 if (ctx->req_info.dev) { 235 ethnl_parse_header_dev_put(&ctx->req_info); 236 ctx->req_info.dev = NULL; 237 } 238 239 return ret; 240 } 241 242 int ethnl_tunnel_info_dumpit(struct sk_buff *skb, struct netlink_callback *cb) 243 { 244 struct ethnl_tunnel_info_dump_ctx *ctx = (void *)cb->ctx; 245 struct net *net = sock_net(skb->sk); 246 int s_idx = ctx->pos_idx; 247 int h, idx = 0; 248 int ret = 0; 249 void *ehdr; 250 251 rtnl_lock(); 252 cb->seq = net->dev_base_seq; 253 for (h = ctx->pos_hash; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 254 struct hlist_head *head; 255 struct net_device *dev; 256 257 head = &net->dev_index_head[h]; 258 idx = 0; 259 hlist_for_each_entry(dev, head, index_hlist) { 260 if (idx < s_idx) 261 goto cont; 262 263 ehdr = ethnl_dump_put(skb, cb, 264 ETHTOOL_MSG_TUNNEL_INFO_GET_REPLY); 265 if (!ehdr) { 266 ret = -EMSGSIZE; 267 goto out; 268 } 269 270 ret = ethnl_fill_reply_header(skb, dev, ETHTOOL_A_TUNNEL_INFO_HEADER); 271 if (ret < 0) { 272 genlmsg_cancel(skb, ehdr); 273 goto out; 274 } 275 276 ctx->req_info.dev = dev; 277 ret = ethnl_tunnel_info_fill_reply(&ctx->req_info, skb); 278 ctx->req_info.dev = NULL; 279 if (ret < 0) { 280 genlmsg_cancel(skb, ehdr); 281 if (ret == -EOPNOTSUPP) 282 goto cont; 283 goto out; 284 } 285 genlmsg_end(skb, ehdr); 286 cont: 287 idx++; 288 } 289 } 290 out: 291 rtnl_unlock(); 292 293 ctx->pos_hash = h; 294 ctx->pos_idx = idx; 295 nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 296 297 if (ret == -EMSGSIZE && skb->len) 298 return skb->len; 299 return ret; 300 } 301