1 /* 2 * lib/route/link/vlan.c VLAN Link Info 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation version 2.1 7 * of the License. 8 * 9 * Copyright (c) 2003-2008 Thomas Graf <tgraf (at) suug.ch> 10 */ 11 12 /** 13 * @ingroup link_info 14 * @defgroup vlan VLAN 15 * @brief 16 * 17 * @{ 18 */ 19 20 #include <netlink-local.h> 21 #include <netlink/netlink.h> 22 #include <netlink/attr.h> 23 #include <netlink/utils.h> 24 #include <netlink/object.h> 25 #include <netlink/route/rtnl.h> 26 #include <netlink/route/link/info-api.h> 27 #include <netlink/route/link/vlan.h> 28 29 #include <linux/if_vlan.h> 30 31 /** @cond SKIP */ 32 #define VLAN_HAS_ID (1<<0) 33 #define VLAN_HAS_FLAGS (1<<1) 34 #define VLAN_HAS_INGRESS_QOS (1<<2) 35 #define VLAN_HAS_EGRESS_QOS (1<<3) 36 37 struct vlan_info 38 { 39 uint16_t vi_vlan_id; 40 uint32_t vi_flags; 41 uint32_t vi_flags_mask; 42 uint32_t vi_ingress_qos[VLAN_PRIO_MAX+1]; 43 uint32_t vi_negress; 44 uint32_t vi_egress_size; 45 struct vlan_map * vi_egress_qos; 46 uint32_t vi_mask; 47 }; 48 /** @endcond */ 49 50 static struct trans_tbl vlan_flags[] = { 51 __ADD(VLAN_FLAG_REORDER_HDR, reorder_hdr) 52 }; 53 54 char *rtnl_link_vlan_flags2str(int flags, char *buf, size_t len) 55 { 56 return __flags2str(flags, buf, len, vlan_flags, ARRAY_SIZE(vlan_flags)); 57 } 58 59 int rtnl_link_vlan_str2flags(const char *name) 60 { 61 return __str2flags(name, vlan_flags, ARRAY_SIZE(vlan_flags)); 62 } 63 64 static struct nla_policy vlan_policy[IFLA_VLAN_MAX+1] = { 65 [IFLA_VLAN_ID] = { .type = NLA_U16 }, 66 [IFLA_VLAN_FLAGS] = { .minlen = sizeof(struct ifla_vlan_flags) }, 67 [IFLA_VLAN_INGRESS_QOS] = { .type = NLA_NESTED }, 68 [IFLA_VLAN_EGRESS_QOS] = { .type = NLA_NESTED }, 69 }; 70 71 static int vlan_alloc(struct rtnl_link *link) 72 { 73 struct vlan_info *vi; 74 75 if ((vi = calloc(1, sizeof(*vi))) == NULL) 76 return -NLE_NOMEM; 77 78 link->l_info = vi; 79 80 return 0; 81 } 82 83 static int vlan_parse(struct rtnl_link *link, struct nlattr *data, 84 struct nlattr *xstats) 85 { 86 struct nlattr *tb[IFLA_VLAN_MAX+1]; 87 struct vlan_info *vi; 88 int err; 89 90 NL_DBG(3, "Parsing VLAN link info"); 91 92 if ((err = nla_parse_nested(tb, IFLA_VLAN_MAX, data, vlan_policy)) < 0) 93 goto errout; 94 95 if ((err = vlan_alloc(link)) < 0) 96 goto errout; 97 98 vi = link->l_info; 99 100 if (tb[IFLA_VLAN_ID]) { 101 vi->vi_vlan_id = nla_get_u16(tb[IFLA_VLAN_ID]); 102 vi->vi_mask |= VLAN_HAS_ID; 103 } 104 105 if (tb[IFLA_VLAN_FLAGS]) { 106 struct ifla_vlan_flags flags; 107 nla_memcpy(&flags, tb[IFLA_VLAN_FLAGS], sizeof(flags)); 108 109 vi->vi_flags = flags.flags; 110 vi->vi_mask |= VLAN_HAS_FLAGS; 111 } 112 113 if (tb[IFLA_VLAN_INGRESS_QOS]) { 114 struct ifla_vlan_qos_mapping *map; 115 struct nlattr *nla; 116 int remaining; 117 118 memset(vi->vi_ingress_qos, 0, sizeof(vi->vi_ingress_qos)); 119 120 nla_for_each_nested(nla, tb[IFLA_VLAN_INGRESS_QOS], remaining) { 121 if (nla_len(nla) < sizeof(*map)) 122 return -NLE_INVAL; 123 124 map = nla_data(nla); 125 if (map->from < 0 || map->from > VLAN_PRIO_MAX) { 126 return -NLE_INVAL; 127 } 128 129 vi->vi_ingress_qos[map->from] = map->to; 130 } 131 132 vi->vi_mask |= VLAN_HAS_INGRESS_QOS; 133 } 134 135 if (tb[IFLA_VLAN_EGRESS_QOS]) { 136 struct ifla_vlan_qos_mapping *map; 137 struct nlattr *nla; 138 int remaining, i = 0; 139 140 nla_for_each_nested(nla, tb[IFLA_VLAN_EGRESS_QOS], remaining) { 141 if (nla_len(nla) < sizeof(*map)) 142 return -NLE_INVAL; 143 i++; 144 } 145 146 /* align to have a little reserve */ 147 vi->vi_egress_size = (i + 32) & ~31; 148 vi->vi_egress_qos = calloc(vi->vi_egress_size, sizeof(*map)); 149 if (vi->vi_egress_qos == NULL) 150 return -NLE_NOMEM; 151 152 i = 0; 153 nla_for_each_nested(nla, tb[IFLA_VLAN_EGRESS_QOS], remaining) { 154 map = nla_data(nla); 155 NL_DBG(4, "Assigning egress qos mapping %d\n", i); 156 vi->vi_egress_qos[i].vm_from = map->from; 157 vi->vi_egress_qos[i++].vm_to = map->to; 158 } 159 160 vi->vi_negress = i; 161 vi->vi_mask |= VLAN_HAS_EGRESS_QOS; 162 } 163 164 err = 0; 165 errout: 166 return err; 167 } 168 169 static void vlan_free(struct rtnl_link *link) 170 { 171 struct vlan_info *vi = link->l_info; 172 173 if (vi) { 174 free(vi->vi_egress_qos); 175 vi->vi_egress_qos = NULL; 176 } 177 178 free(vi); 179 link->l_info = NULL; 180 } 181 182 static void vlan_dump_line(struct rtnl_link *link, struct nl_dump_params *p) 183 { 184 struct vlan_info *vi = link->l_info; 185 186 nl_dump(p, "vlan-id %d", vi->vi_vlan_id); 187 } 188 189 static void vlan_dump_details(struct rtnl_link *link, struct nl_dump_params *p) 190 { 191 struct vlan_info *vi = link->l_info; 192 int i, printed; 193 char buf[64]; 194 195 rtnl_link_vlan_flags2str(vi->vi_flags, buf, sizeof(buf)); 196 nl_dump_line(p, " vlan-info id %d <%s>\n", vi->vi_vlan_id, buf); 197 198 if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) { 199 nl_dump_line(p, 200 " ingress vlan prio -> qos/socket prio mapping:\n"); 201 for (i = 0, printed = 0; i <= VLAN_PRIO_MAX; i++) { 202 if (vi->vi_ingress_qos[i]) { 203 if (printed == 0) 204 nl_dump_line(p, " "); 205 nl_dump(p, "%x -> %#08x, ", 206 i, vi->vi_ingress_qos[i]); 207 if (printed++ == 3) { 208 nl_dump(p, "\n"); 209 printed = 0; 210 } 211 } 212 } 213 214 if (printed > 0 && printed != 4) 215 nl_dump(p, "\n"); 216 } 217 218 if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) { 219 nl_dump_line(p, 220 " egress qos/socket prio -> vlan prio mapping:\n"); 221 for (i = 0, printed = 0; i < vi->vi_negress; i++) { 222 if (printed == 0) 223 nl_dump_line(p, " "); 224 nl_dump(p, "%#08x -> %x, ", 225 vi->vi_egress_qos[i].vm_from, 226 vi->vi_egress_qos[i].vm_to); 227 if (printed++ == 3) { 228 nl_dump(p, "\n"); 229 printed = 0; 230 } 231 } 232 233 if (printed > 0 && printed != 4) 234 nl_dump(p, "\n"); 235 } 236 } 237 238 static int vlan_clone(struct rtnl_link *dst, struct rtnl_link *src) 239 { 240 struct vlan_info *vdst, *vsrc = src->l_info; 241 int err; 242 243 dst->l_info = NULL; 244 if ((err = rtnl_link_set_info_type(dst, "vlan")) < 0) 245 return err; 246 vdst = dst->l_info; 247 248 vdst->vi_egress_qos = calloc(vsrc->vi_egress_size, 249 sizeof(struct vlan_map)); 250 if (!vdst->vi_egress_qos) 251 return -NLE_NOMEM; 252 253 memcpy(vdst->vi_egress_qos, vsrc->vi_egress_qos, 254 vsrc->vi_egress_size * sizeof(struct vlan_map)); 255 256 return 0; 257 } 258 259 static int vlan_put_attrs(struct nl_msg *msg, struct rtnl_link *link) 260 { 261 struct vlan_info *vi = link->l_info; 262 struct nlattr *data; 263 264 if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) 265 return -NLE_MSGSIZE; 266 267 if (vi->vi_mask & VLAN_HAS_ID) 268 NLA_PUT_U16(msg, IFLA_VLAN_ID, vi->vi_vlan_id); 269 270 if (vi->vi_mask & VLAN_HAS_FLAGS) { 271 struct ifla_vlan_flags flags = { 272 .flags = vi->vi_flags, 273 .mask = vi->vi_flags_mask, 274 }; 275 276 NLA_PUT(msg, IFLA_VLAN_FLAGS, sizeof(flags), &flags); 277 } 278 279 if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) { 280 struct ifla_vlan_qos_mapping map; 281 struct nlattr *qos; 282 int i; 283 284 if (!(qos = nla_nest_start(msg, IFLA_VLAN_INGRESS_QOS))) 285 goto nla_put_failure; 286 287 for (i = 0; i <= VLAN_PRIO_MAX; i++) { 288 if (vi->vi_ingress_qos[i]) { 289 map.from = i; 290 map.to = vi->vi_ingress_qos[i]; 291 292 NLA_PUT(msg, i, sizeof(map), &map); 293 } 294 } 295 296 nla_nest_end(msg, qos); 297 } 298 299 if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) { 300 struct ifla_vlan_qos_mapping map; 301 struct nlattr *qos; 302 int i; 303 304 if (!(qos = nla_nest_start(msg, IFLA_VLAN_EGRESS_QOS))) 305 goto nla_put_failure; 306 307 for (i = 0; i < vi->vi_negress; i++) { 308 map.from = vi->vi_egress_qos[i].vm_from; 309 map.to = vi->vi_egress_qos[i].vm_to; 310 311 NLA_PUT(msg, i, sizeof(map), &map); 312 } 313 314 nla_nest_end(msg, qos); 315 } 316 317 nla_nest_end(msg, data); 318 319 nla_put_failure: 320 321 return 0; 322 } 323 324 static struct rtnl_link_info_ops vlan_info_ops = { 325 .io_name = "vlan", 326 .io_alloc = vlan_alloc, 327 .io_parse = vlan_parse, 328 .io_dump = { 329 [NL_DUMP_LINE] = vlan_dump_line, 330 [NL_DUMP_DETAILS] = vlan_dump_details, 331 }, 332 .io_clone = vlan_clone, 333 .io_put_attrs = vlan_put_attrs, 334 .io_free = vlan_free, 335 }; 336 337 int rtnl_link_vlan_set_id(struct rtnl_link *link, int id) 338 { 339 struct vlan_info *vi = link->l_info; 340 341 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) 342 return -NLE_OPNOTSUPP; 343 344 vi->vi_vlan_id = id; 345 vi->vi_mask |= VLAN_HAS_ID; 346 347 return 0; 348 } 349 350 int rtnl_link_vlan_get_id(struct rtnl_link *link) 351 { 352 struct vlan_info *vi = link->l_info; 353 354 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) 355 return -NLE_OPNOTSUPP; 356 357 if (vi->vi_mask & VLAN_HAS_ID) 358 return vi->vi_vlan_id; 359 else 360 return 0; 361 } 362 363 int rtnl_link_vlan_set_flags(struct rtnl_link *link, unsigned int flags) 364 { 365 struct vlan_info *vi = link->l_info; 366 367 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) 368 return -NLE_OPNOTSUPP; 369 370 vi->vi_flags_mask |= flags; 371 vi->vi_flags |= flags; 372 vi->vi_mask |= VLAN_HAS_FLAGS; 373 374 return 0; 375 } 376 377 int rtnl_link_vlan_unset_flags(struct rtnl_link *link, unsigned int flags) 378 { 379 struct vlan_info *vi = link->l_info; 380 381 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) 382 return -NLE_OPNOTSUPP; 383 384 vi->vi_flags_mask |= flags; 385 vi->vi_flags &= ~flags; 386 vi->vi_mask |= VLAN_HAS_FLAGS; 387 388 return 0; 389 } 390 391 unsigned int rtnl_link_vlan_get_flags(struct rtnl_link *link) 392 { 393 struct vlan_info *vi = link->l_info; 394 395 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) 396 return -NLE_OPNOTSUPP; 397 398 return vi->vi_flags; 399 } 400 401 int rtnl_link_vlan_set_ingress_map(struct rtnl_link *link, int from, 402 uint32_t to) 403 { 404 struct vlan_info *vi = link->l_info; 405 406 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) 407 return -NLE_OPNOTSUPP; 408 409 if (from < 0 || from > VLAN_PRIO_MAX) 410 return -NLE_INVAL; 411 412 vi->vi_ingress_qos[from] = to; 413 vi->vi_mask |= VLAN_HAS_INGRESS_QOS; 414 415 return 0; 416 } 417 418 uint32_t *rtnl_link_vlan_get_ingress_map(struct rtnl_link *link) 419 { 420 struct vlan_info *vi = link->l_info; 421 422 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) 423 return NULL; 424 425 if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) 426 return vi->vi_ingress_qos; 427 else 428 return NULL; 429 } 430 431 int rtnl_link_vlan_set_egress_map(struct rtnl_link *link, uint32_t from, int to) 432 { 433 struct vlan_info *vi = link->l_info; 434 435 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) 436 return -NLE_OPNOTSUPP; 437 438 if (to < 0 || to > VLAN_PRIO_MAX) 439 return -NLE_INVAL; 440 441 if (vi->vi_negress >= vi->vi_egress_size) { 442 int new_size = vi->vi_egress_size + 32; 443 void *ptr; 444 445 ptr = realloc(vi->vi_egress_qos, new_size); 446 if (!ptr) 447 return -NLE_NOMEM; 448 449 vi->vi_egress_qos = ptr; 450 vi->vi_egress_size = new_size; 451 } 452 453 vi->vi_egress_qos[vi->vi_negress].vm_from = from; 454 vi->vi_egress_qos[vi->vi_negress].vm_to = to; 455 vi->vi_negress++; 456 vi->vi_mask |= VLAN_HAS_EGRESS_QOS; 457 458 return 0; 459 } 460 461 struct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *link, 462 int *negress) 463 { 464 struct vlan_info *vi = link->l_info; 465 466 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) 467 return NULL; 468 469 if (negress == NULL) 470 return NULL; 471 472 if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) { 473 *negress = vi->vi_negress; 474 return vi->vi_egress_qos; 475 } else { 476 *negress = 0; 477 return NULL; 478 } 479 } 480 481 static void __init vlan_init(void) 482 { 483 rtnl_link_register_info(&vlan_info_ops); 484 } 485 486 static void __exit vlan_exit(void) 487 { 488 rtnl_link_unregister_info(&vlan_info_ops); 489 } 490 491 /** @} */ 492