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