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@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 26extern struct ematch_util meta_ematch_util; 27 28static 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(nfmark 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 42struct 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 114static 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 125static struct meta_entry * lookup_meta_entry(struct bstr *kind) 126{ 127 int i; 128 129 for (i = 0; i < (sizeof(meta_table)/sizeof(meta_table[0])); 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 137static struct meta_entry * lookup_meta_entry_byid(int id) 138{ 139 int i; 140 141 for (i = 0; i < (sizeof(meta_table)/sizeof(meta_table[0])); i++) 142 if (meta_table[i].id == id) 143 return &meta_table[i]; 144 145 return NULL; 146} 147 148static 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 addattr_l(n, MAX_MSG, tlv, a->data, a->len); 163 } 164 break; 165 } 166} 167 168static inline int is_compatible(struct tcf_meta_val *what, 169 struct tcf_meta_val *needed) 170{ 171 char *p; 172 struct meta_entry *entry; 173 174 entry = lookup_meta_entry_byid(TCF_META_ID(what->kind)); 175 176 if (entry == NULL) 177 return 0; 178 179 for (p = entry->mask; p; p++) 180 if (map_type(*p) == TCF_META_TYPE(needed->kind)) 181 return 1; 182 183 return 0; 184} 185 186static void list_meta_ids(FILE *fd) 187{ 188 int i; 189 190 fprintf(fd, 191 "--------------------------------------------------------\n" \ 192 " ID Type Description\n" \ 193 "--------------------------------------------------------"); 194 195 for (i = 0; i < (sizeof(meta_table)/sizeof(meta_table[0])); i++) { 196 if (meta_table[i].id == TCF_META_ID_SECTION) { 197 fprintf(fd, "\n%s:\n", meta_table[i].kind); 198 } else { 199 char *p = meta_table[i].mask; 200 char buf[64] = {0}; 201 202 fprintf(fd, " %-16s ", meta_table[i].kind); 203 204 while (*p) { 205 int type = map_type(*p); 206 207 switch (type) { 208 case TCF_META_TYPE_INT: 209 strcat(buf, "INT"); 210 break; 211 212 case TCF_META_TYPE_VAR: 213 strcat(buf, "VAR"); 214 break; 215 } 216 217 if (*(++p)) 218 strcat(buf, ","); 219 } 220 221 fprintf(fd, "%-10s %s\n", buf, meta_table[i].desc); 222 } 223 } 224 225 fprintf(fd, 226 "--------------------------------------------------------\n"); 227} 228 229#undef TCF_META_ID_SECTION 230 231#define PARSE_FAILURE ((void *) (-1)) 232 233#define PARSE_ERR(CARG, FMT, ARGS...) \ 234 em_parse_error(EINVAL, args, CARG, &meta_ematch_util, FMT ,##ARGS) 235 236static inline int can_adopt(struct tcf_meta_val *val) 237{ 238 return !!TCF_META_ID(val->kind); 239} 240 241static inline int overwrite_type(struct tcf_meta_val *src, 242 struct tcf_meta_val *dst) 243{ 244 return (TCF_META_TYPE(dst->kind) << 12) | TCF_META_ID(src->kind); 245} 246 247 248static inline struct bstr * 249parse_object(struct bstr *args, struct bstr *arg, struct tcf_meta_val *obj, 250 unsigned long *dst, struct tcf_meta_val *left) 251{ 252 struct meta_entry *entry; 253 unsigned long num; 254 struct bstr *a; 255 256 if (arg->quoted) { 257 obj->kind = TCF_META_TYPE_VAR << 12; 258 obj->kind |= TCF_META_ID_VALUE; 259 *dst = (unsigned long) arg; 260 return bstr_next(arg); 261 } 262 263 num = bstrtoul(arg); 264 if (num != ULONG_MAX) { 265 obj->kind = TCF_META_TYPE_INT << 12; 266 obj->kind |= TCF_META_ID_VALUE; 267 *dst = (unsigned long) num; 268 return bstr_next(arg); 269 } 270 271 entry = lookup_meta_entry(arg); 272 273 if (entry == NULL) { 274 PARSE_ERR(arg, "meta: unknown meta id\n"); 275 return PARSE_FAILURE; 276 } 277 278 obj->kind = entry->id | (map_type(entry->mask[0]) << 12); 279 280 if (left) { 281 struct tcf_meta_val *right = obj; 282 283 if (TCF_META_TYPE(right->kind) == TCF_META_TYPE(left->kind)) 284 goto compatible; 285 286 if (can_adopt(left) && !can_adopt(right)) { 287 if (is_compatible(left, right)) 288 left->kind = overwrite_type(left, right); 289 else 290 goto not_compatible; 291 } else if (can_adopt(right) && !can_adopt(left)) { 292 if (is_compatible(right, left)) 293 right->kind = overwrite_type(right, left); 294 else 295 goto not_compatible; 296 } else if (can_adopt(left) && can_adopt(right)) { 297 if (is_compatible(left, right)) 298 left->kind = overwrite_type(left, right); 299 else if (is_compatible(right, left)) 300 right->kind = overwrite_type(right, left); 301 else 302 goto not_compatible; 303 } else 304 goto not_compatible; 305 } 306 307compatible: 308 309 a = bstr_next(arg); 310 311 while(a) { 312 if (!bstrcmp(a, "shift")) { 313 unsigned long shift; 314 315 if (a->next == NULL) { 316 PARSE_ERR(a, "meta: missing argument"); 317 return PARSE_FAILURE; 318 } 319 a = bstr_next(a); 320 321 shift = bstrtoul(a); 322 if (shift == ULONG_MAX) { 323 PARSE_ERR(a, "meta: invalid shift, must " \ 324 "be numeric"); 325 return PARSE_FAILURE; 326 } 327 328 obj->shift = (__u8) shift; 329 a = bstr_next(a); 330 } else if (!bstrcmp(a, "mask")) { 331 unsigned long mask; 332 333 if (a->next == NULL) { 334 PARSE_ERR(a, "meta: missing argument"); 335 return PARSE_FAILURE; 336 } 337 a = bstr_next(a); 338 339 mask = bstrtoul(a); 340 if (mask == ULONG_MAX) { 341 PARSE_ERR(a, "meta: invalid mask, must be " \ 342 "numeric"); 343 return PARSE_FAILURE; 344 } 345 *dst = (unsigned long) mask; 346 a = bstr_next(a); 347 } else 348 break; 349 } 350 351 return a; 352 353not_compatible: 354 PARSE_ERR(arg, "lvalue and rvalue are not compatible."); 355 return PARSE_FAILURE; 356} 357 358static int meta_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr, 359 struct bstr *args) 360{ 361 int opnd; 362 struct bstr *a; 363 struct tcf_meta_hdr meta_hdr; 364 unsigned long lvalue = 0, rvalue = 0; 365 366 memset(&meta_hdr, 0, sizeof(meta_hdr)); 367 368 if (args == NULL) 369 return PARSE_ERR(args, "meta: missing arguments"); 370 371 if (!bstrcmp(args, "list")) { 372 list_meta_ids(stderr); 373 return -1; 374 } 375 376 a = parse_object(args, args, &meta_hdr.left, &lvalue, NULL); 377 if (a == PARSE_FAILURE) 378 return -1; 379 else if (a == NULL) 380 return PARSE_ERR(args, "meta: missing operand"); 381 382 if (!bstrcmp(a, "eq")) 383 opnd = TCF_EM_OPND_EQ; 384 else if (!bstrcmp(a, "gt")) 385 opnd = TCF_EM_OPND_GT; 386 else if (!bstrcmp(a, "lt")) 387 opnd = TCF_EM_OPND_LT; 388 else 389 return PARSE_ERR(a, "meta: invalid operand"); 390 391 meta_hdr.left.op = (__u8) opnd; 392 393 if (a->next == NULL) 394 return PARSE_ERR(args, "meta: missing rvalue"); 395 a = bstr_next(a); 396 397 a = parse_object(args, a, &meta_hdr.right, &rvalue, &meta_hdr.left); 398 if (a == PARSE_FAILURE) 399 return -1; 400 else if (a != NULL) 401 return PARSE_ERR(a, "meta: unexpected trailer"); 402 403 404 addraw_l(n, MAX_MSG, hdr, sizeof(*hdr)); 405 406 addattr_l(n, MAX_MSG, TCA_EM_META_HDR, &meta_hdr, sizeof(meta_hdr)); 407 408 dump_value(n, TCA_EM_META_LVALUE, lvalue, &meta_hdr.left); 409 dump_value(n, TCA_EM_META_RVALUE, rvalue, &meta_hdr.right); 410 411 return 0; 412} 413#undef PARSE_ERR 414 415static inline void print_binary(FILE *fd, unsigned char *str, int len) 416{ 417 int i; 418 419 for (i = 0; i < len; i++) 420 if (!isprint(str[i])) 421 goto binary; 422 423 for (i = 0; i < len; i++) 424 fprintf(fd, "%c", str[i]); 425 return; 426 427binary: 428 for (i = 0; i < len; i++) 429 fprintf(fd, "%02x ", str[i]); 430 431 fprintf(fd, "\""); 432 for (i = 0; i < len; i++) 433 fprintf(fd, "%c", isprint(str[i]) ? str[i] : '.'); 434 fprintf(fd, "\""); 435} 436 437static inline int print_value(FILE *fd, int type, struct rtattr *rta) 438{ 439 if (rta == NULL) { 440 fprintf(stderr, "Missing value TLV\n"); 441 return -1; 442 } 443 444 switch(type) { 445 case TCF_META_TYPE_INT: 446 if (RTA_PAYLOAD(rta) < sizeof(__u32)) { 447 fprintf(stderr, "meta int type value TLV " \ 448 "size mismatch.\n"); 449 return -1; 450 } 451 fprintf(fd, "%d", rta_getattr_u32(rta)); 452 break; 453 454 case TCF_META_TYPE_VAR: 455 print_binary(fd, RTA_DATA(rta), RTA_PAYLOAD(rta)); 456 break; 457 } 458 459 return 0; 460} 461 462static int print_object(FILE *fd, struct tcf_meta_val *obj, struct rtattr *rta) 463{ 464 int id = TCF_META_ID(obj->kind); 465 int type = TCF_META_TYPE(obj->kind); 466 struct meta_entry *entry; 467 468 if (id == TCF_META_ID_VALUE) 469 return print_value(fd, type, rta); 470 471 entry = lookup_meta_entry_byid(id); 472 473 if (entry == NULL) 474 fprintf(fd, "[unknown meta id %d]", id); 475 else 476 fprintf(fd, "%s", entry->kind); 477 478 if (obj->shift) 479 fprintf(fd, " shift %d", obj->shift); 480 481 switch (type) { 482 case TCF_META_TYPE_INT: 483 if (rta) { 484 if (RTA_PAYLOAD(rta) < sizeof(__u32)) 485 goto size_mismatch; 486 487 fprintf(fd, " mask 0x%08x", 488 rta_getattr_u32(rta)); 489 } 490 break; 491 } 492 493 return 0; 494 495size_mismatch: 496 fprintf(stderr, "meta int type mask TLV size mismatch\n"); 497 return -1; 498} 499 500 501static 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 540struct 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