1 /* 2 * em_meta.c Metadata Ematch 3 * 4 * This program is free software; you can distribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 7 * 2 of the License, or (at your option) any later version. 8 * 9 * Authors: Thomas Graf <tgraf (at) suug.ch> 10 */ 11 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <unistd.h> 15 #include <syslog.h> 16 #include <fcntl.h> 17 #include <sys/socket.h> 18 #include <netinet/in.h> 19 #include <arpa/inet.h> 20 #include <string.h> 21 #include <errno.h> 22 23 #include "m_ematch.h" 24 #include <linux/tc_ematch/tc_em_meta.h> 25 26 extern struct ematch_util meta_ematch_util; 27 28 static void meta_print_usage(FILE *fd) 29 { 30 fprintf(fd, 31 "Usage: meta(OBJECT { eq | lt | gt } OBJECT)\n" \ 32 "where: OBJECT := { META_ID | VALUE }\n" \ 33 " META_ID := id [ shift SHIFT ] [ mask MASK ]\n" \ 34 "\n" \ 35 "Example: meta(nf_mark gt 24)\n" \ 36 " meta(indev shift 1 eq \"ppp\")\n" \ 37 " meta(tcindex mask 0xf0 eq 0xf0)\n" \ 38 "\n" \ 39 "For a list of meta identifiers, use meta(list).\n"); 40 } 41 42 struct meta_entry { 43 int id; 44 char *kind; 45 char *mask; 46 char *desc; 47 } meta_table[] = { 48 #define TCF_META_ID_SECTION 0 49 #define __A(id, name, mask, desc) { TCF_META_ID_##id, name, mask, desc } 50 __A(SECTION, "Generic", "", ""), 51 __A(RANDOM, "random", "i", 52 "Random value (32 bit)"), 53 __A(LOADAVG_0, "loadavg_1", "i", 54 "Load average in last minute"), 55 __A(LOADAVG_1, "loadavg_5", "i", 56 "Load average in last 5 minutes"), 57 __A(LOADAVG_2, "loadavg_15", "i", 58 "Load average in last 15 minutes"), 59 60 __A(SECTION, "Interfaces", "", ""), 61 __A(DEV, "dev", "iv", 62 "Device the packet is on"), 63 __A(SECTION, "Packet attributes", "", ""), 64 __A(PRIORITY, "priority", "i", 65 "Priority of packet"), 66 __A(PROTOCOL, "protocol", "i", 67 "Link layer protocol"), 68 __A(PKTTYPE, "pkt_type", "i", 69 "Packet type (uni|multi|broad|...)cast"), 70 __A(PKTLEN, "pkt_len", "i", 71 "Length of packet"), 72 __A(DATALEN, "data_len", "i", 73 "Length of data in packet"), 74 __A(MACLEN, "mac_len", "i", 75 "Length of link layer header"), 76 77 __A(SECTION, "Netfilter", "", ""), 78 __A(NFMARK, "nf_mark", "i", 79 "Netfilter mark"), 80 __A(NFMARK, "fwmark", "i", 81 "Alias for nf_mark"), 82 83 __A(SECTION, "Traffic Control", "", ""), 84 __A(TCINDEX, "tc_index", "i", "TC Index"), 85 __A(SECTION, "Routing", "", ""), 86 __A(RTCLASSID, "rt_classid", "i", 87 "Routing ClassID (cls_route)"), 88 __A(RTIIF, "rt_iif", "i", 89 "Incoming interface index"), 90 __A(VLAN_TAG, "vlan", "i", "Vlan tag"), 91 92 __A(SECTION, "Sockets", "", ""), 93 __A(SK_FAMILY, "sk_family", "i", "Address family"), 94 __A(SK_STATE, "sk_state", "i", "State"), 95 __A(SK_REUSE, "sk_reuse", "i", "Reuse Flag"), 96 __A(SK_BOUND_IF, "sk_bind_if", "iv", "Bound interface"), 97 __A(SK_REFCNT, "sk_refcnt", "i", "Reference counter"), 98 __A(SK_SHUTDOWN, "sk_shutdown", "i", "Shutdown mask"), 99 __A(SK_PROTO, "sk_proto", "i", "Protocol"), 100 __A(SK_TYPE, "sk_type", "i", "Type"), 101 __A(SK_RCVBUF, "sk_rcvbuf", "i", "Receive buffer size"), 102 __A(SK_RMEM_ALLOC, "sk_rmem", "i", "RMEM"), 103 __A(SK_WMEM_ALLOC, "sk_wmem", "i", "WMEM"), 104 __A(SK_OMEM_ALLOC, "sk_omem", "i", "OMEM"), 105 __A(SK_WMEM_QUEUED, "sk_wmem_queue", "i", "WMEM queue"), 106 __A(SK_SND_QLEN, "sk_snd_queue", "i", "Send queue length"), 107 __A(SK_RCV_QLEN, "sk_rcv_queue", "i", "Receive queue length"), 108 __A(SK_ERR_QLEN, "sk_err_queue", "i", "Error queue length"), 109 __A(SK_FORWARD_ALLOCS, "sk_fwd_alloc", "i", "Forward allocations"), 110 __A(SK_SNDBUF, "sk_sndbuf", "i", "Send buffer size"), 111 #undef __A 112 }; 113 114 static inline int map_type(char k) 115 { 116 switch (k) { 117 case 'i': return TCF_META_TYPE_INT; 118 case 'v': return TCF_META_TYPE_VAR; 119 } 120 121 fprintf(stderr, "BUG: Unknown map character '%c'\n", k); 122 return INT_MAX; 123 } 124 125 static struct meta_entry *lookup_meta_entry(struct bstr *kind) 126 { 127 int i; 128 129 for (i = 0; i < ARRAY_SIZE(meta_table); i++) 130 if (!bstrcmp(kind, meta_table[i].kind) && 131 meta_table[i].id != 0) 132 return &meta_table[i]; 133 134 return NULL; 135 } 136 137 static struct meta_entry *lookup_meta_entry_byid(int id) 138 { 139 int i; 140 141 for (i = 0; i < ARRAY_SIZE(meta_table); i++) 142 if (meta_table[i].id == id) 143 return &meta_table[i]; 144 145 return NULL; 146 } 147 148 static inline void dump_value(struct nlmsghdr *n, int tlv, unsigned long val, 149 struct tcf_meta_val *hdr) 150 { 151 __u32 t; 152 153 switch (TCF_META_TYPE(hdr->kind)) { 154 case TCF_META_TYPE_INT: 155 t = val; 156 addattr_l(n, MAX_MSG, tlv, &t, sizeof(t)); 157 break; 158 159 case TCF_META_TYPE_VAR: 160 if (TCF_META_ID(hdr->kind) == TCF_META_ID_VALUE) { 161 struct bstr *a = (struct bstr *) val; 162 163 addattr_l(n, MAX_MSG, tlv, a->data, a->len); 164 } 165 break; 166 } 167 } 168 169 static inline int is_compatible(struct tcf_meta_val *what, 170 struct tcf_meta_val *needed) 171 { 172 char *p; 173 struct meta_entry *entry; 174 175 entry = lookup_meta_entry_byid(TCF_META_ID(what->kind)); 176 177 if (entry == NULL) 178 return 0; 179 180 for (p = entry->mask; p; p++) 181 if (map_type(*p) == TCF_META_TYPE(needed->kind)) 182 return 1; 183 184 return 0; 185 } 186 187 static void list_meta_ids(FILE *fd) 188 { 189 int i; 190 191 fprintf(fd, 192 "--------------------------------------------------------\n" \ 193 " ID Type Description\n" \ 194 "--------------------------------------------------------"); 195 196 for (i = 0; i < ARRAY_SIZE(meta_table); i++) { 197 if (meta_table[i].id == TCF_META_ID_SECTION) { 198 fprintf(fd, "\n%s:\n", meta_table[i].kind); 199 } else { 200 char *p = meta_table[i].mask; 201 char buf[64] = {0}; 202 203 fprintf(fd, " %-16s ", meta_table[i].kind); 204 205 while (*p) { 206 int type = map_type(*p); 207 208 switch (type) { 209 case TCF_META_TYPE_INT: 210 strcat(buf, "INT"); 211 break; 212 213 case TCF_META_TYPE_VAR: 214 strcat(buf, "VAR"); 215 break; 216 } 217 218 if (*(++p)) 219 strcat(buf, ","); 220 } 221 222 fprintf(fd, "%-10s %s\n", buf, meta_table[i].desc); 223 } 224 } 225 226 fprintf(fd, 227 "--------------------------------------------------------\n"); 228 } 229 230 #undef TCF_META_ID_SECTION 231 232 #define PARSE_FAILURE ((void *) (-1)) 233 234 #define PARSE_ERR(CARG, FMT, ARGS...) \ 235 em_parse_error(EINVAL, args, CARG, &meta_ematch_util, FMT, ##ARGS) 236 237 static inline int can_adopt(struct tcf_meta_val *val) 238 { 239 return !!TCF_META_ID(val->kind); 240 } 241 242 static inline int overwrite_type(struct tcf_meta_val *src, 243 struct tcf_meta_val *dst) 244 { 245 return (TCF_META_TYPE(dst->kind) << 12) | TCF_META_ID(src->kind); 246 } 247 248 249 static inline struct bstr * 250 parse_object(struct bstr *args, struct bstr *arg, struct tcf_meta_val *obj, 251 unsigned long *dst, struct tcf_meta_val *left) 252 { 253 struct meta_entry *entry; 254 unsigned long num; 255 struct bstr *a; 256 257 if (arg->quoted) { 258 obj->kind = TCF_META_TYPE_VAR << 12; 259 obj->kind |= TCF_META_ID_VALUE; 260 *dst = (unsigned long) arg; 261 return bstr_next(arg); 262 } 263 264 num = bstrtoul(arg); 265 if (num != ULONG_MAX) { 266 obj->kind = TCF_META_TYPE_INT << 12; 267 obj->kind |= TCF_META_ID_VALUE; 268 *dst = (unsigned long) num; 269 return bstr_next(arg); 270 } 271 272 entry = lookup_meta_entry(arg); 273 274 if (entry == NULL) { 275 PARSE_ERR(arg, "meta: unknown meta id\n"); 276 return PARSE_FAILURE; 277 } 278 279 obj->kind = entry->id | (map_type(entry->mask[0]) << 12); 280 281 if (left) { 282 struct tcf_meta_val *right = obj; 283 284 if (TCF_META_TYPE(right->kind) == TCF_META_TYPE(left->kind)) 285 goto compatible; 286 287 if (can_adopt(left) && !can_adopt(right)) { 288 if (is_compatible(left, right)) 289 left->kind = overwrite_type(left, right); 290 else 291 goto not_compatible; 292 } else if (can_adopt(right) && !can_adopt(left)) { 293 if (is_compatible(right, left)) 294 right->kind = overwrite_type(right, left); 295 else 296 goto not_compatible; 297 } else if (can_adopt(left) && can_adopt(right)) { 298 if (is_compatible(left, right)) 299 left->kind = overwrite_type(left, right); 300 else if (is_compatible(right, left)) 301 right->kind = overwrite_type(right, left); 302 else 303 goto not_compatible; 304 } else 305 goto not_compatible; 306 } 307 308 compatible: 309 310 a = bstr_next(arg); 311 312 while (a) { 313 if (!bstrcmp(a, "shift")) { 314 unsigned long shift; 315 316 if (a->next == NULL) { 317 PARSE_ERR(a, "meta: missing argument"); 318 return PARSE_FAILURE; 319 } 320 a = bstr_next(a); 321 322 shift = bstrtoul(a); 323 if (shift == ULONG_MAX) { 324 PARSE_ERR(a, "meta: invalid shift, must " \ 325 "be numeric"); 326 return PARSE_FAILURE; 327 } 328 329 obj->shift = (__u8) shift; 330 a = bstr_next(a); 331 } else if (!bstrcmp(a, "mask")) { 332 unsigned long mask; 333 334 if (a->next == NULL) { 335 PARSE_ERR(a, "meta: missing argument"); 336 return PARSE_FAILURE; 337 } 338 a = bstr_next(a); 339 340 mask = bstrtoul(a); 341 if (mask == ULONG_MAX) { 342 PARSE_ERR(a, "meta: invalid mask, must be " \ 343 "numeric"); 344 return PARSE_FAILURE; 345 } 346 *dst = (unsigned long) mask; 347 a = bstr_next(a); 348 } else 349 break; 350 } 351 352 return a; 353 354 not_compatible: 355 PARSE_ERR(arg, "lvalue and rvalue are not compatible."); 356 return PARSE_FAILURE; 357 } 358 359 static int meta_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr, 360 struct bstr *args) 361 { 362 int opnd; 363 struct bstr *a; 364 struct tcf_meta_hdr meta_hdr = {}; 365 unsigned long lvalue = 0, rvalue = 0; 366 367 if (args == NULL) 368 return PARSE_ERR(args, "meta: missing arguments"); 369 370 if (!bstrcmp(args, "list")) { 371 list_meta_ids(stderr); 372 return -1; 373 } 374 375 a = parse_object(args, args, &meta_hdr.left, &lvalue, NULL); 376 if (a == PARSE_FAILURE) 377 return -1; 378 else if (a == NULL) 379 return PARSE_ERR(args, "meta: missing operand"); 380 381 if (!bstrcmp(a, "eq")) 382 opnd = TCF_EM_OPND_EQ; 383 else if (!bstrcmp(a, "gt")) 384 opnd = TCF_EM_OPND_GT; 385 else if (!bstrcmp(a, "lt")) 386 opnd = TCF_EM_OPND_LT; 387 else 388 return PARSE_ERR(a, "meta: invalid operand"); 389 390 meta_hdr.left.op = (__u8) opnd; 391 392 if (a->next == NULL) 393 return PARSE_ERR(args, "meta: missing rvalue"); 394 a = bstr_next(a); 395 396 a = parse_object(args, a, &meta_hdr.right, &rvalue, &meta_hdr.left); 397 if (a == PARSE_FAILURE) 398 return -1; 399 else if (a != NULL) 400 return PARSE_ERR(a, "meta: unexpected trailer"); 401 402 403 addraw_l(n, MAX_MSG, hdr, sizeof(*hdr)); 404 405 addattr_l(n, MAX_MSG, TCA_EM_META_HDR, &meta_hdr, sizeof(meta_hdr)); 406 407 dump_value(n, TCA_EM_META_LVALUE, lvalue, &meta_hdr.left); 408 dump_value(n, TCA_EM_META_RVALUE, rvalue, &meta_hdr.right); 409 410 return 0; 411 } 412 #undef PARSE_ERR 413 414 static inline void print_binary(FILE *fd, unsigned char *str, int len) 415 { 416 int i; 417 418 for (i = 0; i < len; i++) 419 if (!isprint(str[i])) 420 goto binary; 421 422 for (i = 0; i < len; i++) 423 fprintf(fd, "%c", str[i]); 424 return; 425 426 binary: 427 for (i = 0; i < len; i++) 428 fprintf(fd, "%02x ", str[i]); 429 430 fprintf(fd, "\""); 431 for (i = 0; i < len; i++) 432 fprintf(fd, "%c", isprint(str[i]) ? str[i] : '.'); 433 fprintf(fd, "\""); 434 } 435 436 static inline int print_value(FILE *fd, int type, struct rtattr *rta) 437 { 438 if (rta == NULL) { 439 fprintf(stderr, "Missing value TLV\n"); 440 return -1; 441 } 442 443 switch (type) { 444 case TCF_META_TYPE_INT: 445 if (RTA_PAYLOAD(rta) < sizeof(__u32)) { 446 fprintf(stderr, "meta int type value TLV " \ 447 "size mismatch.\n"); 448 return -1; 449 } 450 fprintf(fd, "%d", rta_getattr_u32(rta)); 451 break; 452 453 case TCF_META_TYPE_VAR: 454 print_binary(fd, RTA_DATA(rta), RTA_PAYLOAD(rta)); 455 break; 456 } 457 458 return 0; 459 } 460 461 static int print_object(FILE *fd, struct tcf_meta_val *obj, struct rtattr *rta) 462 { 463 int id = TCF_META_ID(obj->kind); 464 int type = TCF_META_TYPE(obj->kind); 465 struct meta_entry *entry; 466 467 if (id == TCF_META_ID_VALUE) 468 return print_value(fd, type, rta); 469 470 entry = lookup_meta_entry_byid(id); 471 472 if (entry == NULL) 473 fprintf(fd, "[unknown meta id %d]", id); 474 else 475 fprintf(fd, "%s", entry->kind); 476 477 if (obj->shift) 478 fprintf(fd, " shift %d", obj->shift); 479 480 switch (type) { 481 case TCF_META_TYPE_INT: 482 if (rta) { 483 if (RTA_PAYLOAD(rta) < sizeof(__u32)) 484 goto size_mismatch; 485 486 if (rta_getattr_u32(rta)) 487 fprintf(fd, " mask 0x%08x", 488 rta_getattr_u32(rta)); 489 } 490 break; 491 } 492 493 return 0; 494 495 size_mismatch: 496 fprintf(stderr, "meta int type mask TLV size mismatch\n"); 497 return -1; 498 } 499 500 501 static int meta_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data, 502 int data_len) 503 { 504 struct rtattr *tb[TCA_EM_META_MAX+1]; 505 struct tcf_meta_hdr *meta_hdr; 506 507 if (parse_rtattr(tb, TCA_EM_META_MAX, data, data_len) < 0) 508 return -1; 509 510 if (tb[TCA_EM_META_HDR] == NULL) { 511 fprintf(stderr, "Missing meta header\n"); 512 return -1; 513 } 514 515 if (RTA_PAYLOAD(tb[TCA_EM_META_HDR]) < sizeof(*meta_hdr)) { 516 fprintf(stderr, "Meta header size mismatch\n"); 517 return -1; 518 } 519 520 meta_hdr = RTA_DATA(tb[TCA_EM_META_HDR]); 521 522 if (print_object(fd, &meta_hdr->left, tb[TCA_EM_META_LVALUE]) < 0) 523 return -1; 524 525 switch (meta_hdr->left.op) { 526 case TCF_EM_OPND_EQ: 527 fprintf(fd, " eq "); 528 break; 529 case TCF_EM_OPND_LT: 530 fprintf(fd, " lt "); 531 break; 532 case TCF_EM_OPND_GT: 533 fprintf(fd, " gt "); 534 break; 535 } 536 537 return print_object(fd, &meta_hdr->right, tb[TCA_EM_META_RVALUE]); 538 } 539 540 struct ematch_util meta_ematch_util = { 541 .kind = "meta", 542 .kind_num = TCF_EM_META, 543 .parse_eopt = meta_parse_eopt, 544 .print_eopt = meta_print_eopt, 545 .print_usage = meta_print_usage 546 }; 547