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 <dlfcn.h> 22#include <errno.h> 23 24#include "m_ematch.h" 25#include <linux/tc_ematch/tc_em_meta.h> 26 27extern struct ematch_util meta_ematch_util; 28 29static void meta_print_usage(FILE *fd) 30{ 31 fprintf(fd, 32 "Usage: meta(OBJECT { eq | lt | gt } OBJECT)\n" \ 33 "where: OBJECT := { META_ID | VALUE }\n" \ 34 " META_ID := id [ shift SHIFT ] [ mask MASK ]\n" \ 35 "\n" \ 36 "Example: meta(nfmark gt 24)\n" \ 37 " meta(indev shift 1 eq \"ppp\"\n" \ 38 " meta(tcindex mask 0xf0 eq 0xf0)\n" \ 39 " meta(dev eq indev)\n" \ 40 "\n" \ 41 "For a list of meta identifiers, use meta(list).\n"); 42} 43 44struct meta_entry { 45 int id; 46 char * kind; 47 char * mask; 48 char * desc; 49} meta_table[] = { 50#define TCF_META_ID_SECTION 0 51#define __A(id, name, mask, desc) { TCF_META_ID_##id, name, mask, desc } 52 __A(SECTION, "Generic", "", ""), 53 __A(RANDOM, "random", "i", 54 "Random value (32 bit)"), 55 __A(LOADAVG_0, "loadavg_1", "i", 56 "Load average in last minute"), 57 __A(LOADAVG_1, "loadavg_5", "i", 58 "Load average in last 5 minutes"), 59 __A(LOADAVG_2, "loadavg_15", "i", 60 "Load average in last 15 minutes"), 61 62 __A(SECTION, "Interfaces", "", ""), 63 __A(DEV, "dev", "iv", 64 "Device the packet is on"), 65 __A(SECTION, "Packet attributes", "", ""), 66 __A(PRIORITY, "priority", "i", 67 "Priority of packet"), 68 __A(PROTOCOL, "protocol", "i", 69 "Link layer protocol"), 70 __A(PKTTYPE, "pkt_type", "i", 71 "Packet type (uni|multi|broad|...)cast"), 72 __A(PKTLEN, "pkt_len", "i", 73 "Length of packet"), 74 __A(DATALEN, "data_len", "i", 75 "Length of data in packet"), 76 __A(MACLEN, "mac_len", "i", 77 "Length of link layer header"), 78 79 __A(SECTION, "Netfilter", "", ""), 80 __A(NFMARK, "nf_mark", "i", 81 "Netfilter mark"), 82 __A(NFMARK, "fwmark", "i", 83 "Alias for nf_mark"), 84 85 __A(SECTION, "Traffic Control", "", ""), 86 __A(TCINDEX, "tc_index", "i", "TC Index"), 87 __A(SECTION, "Routing", "", ""), 88 __A(RTCLASSID, "rt_classid", "i", 89 "Routing ClassID (cls_route)"), 90 __A(RTIIF, "rt_iif", "i", 91 "Incoming interface index"), 92 93 __A(SECTION, "Sockets", "", ""), 94 __A(SK_FAMILY, "sk_family", "i", "Address family"), 95 __A(SK_STATE, "sk_state", "i", "State"), 96 __A(SK_REUSE, "sk_reuse", "i", "Reuse Flag"), 97 __A(SK_BOUND_IF, "sk_bind_if", "iv", "Bound interface"), 98 __A(SK_REFCNT, "sk_refcnt", "i", "Reference counter"), 99 __A(SK_SHUTDOWN, "sk_shutdown", "i", "Shutdown mask"), 100 __A(SK_PROTO, "sk_proto", "i", "Protocol"), 101 __A(SK_TYPE, "sk_type", "i", "Type"), 102 __A(SK_RCVBUF, "sk_rcvbuf", "i", "Receive buffer size"), 103 __A(SK_RMEM_ALLOC, "sk_rmem", "i", "RMEM"), 104 __A(SK_WMEM_ALLOC, "sk_wmem", "i", "WMEM"), 105 __A(SK_OMEM_ALLOC, "sk_omem", "i", "OMEM"), 106 __A(SK_WMEM_QUEUED, "sk_wmem_queue","i", "WMEM queue"), 107 __A(SK_SND_QLEN, "sk_snd_queue", "i", "Send queue length"), 108 __A(SK_RCV_QLEN, "sk_rcv_queue", "i", "Receive queue length"), 109 __A(SK_ERR_QLEN, "sk_err_queue", "i", "Error queue length"), 110 __A(SK_FORWARD_ALLOCS, "sk_fwd_alloc", "i", "Forward allocations"), 111 __A(SK_SNDBUF, "sk_sndbuf", "i", "Send buffer size"), 112#undef __A 113}; 114 115static inline int map_type(char k) 116{ 117 switch (k) { 118 case 'i': return TCF_META_TYPE_INT; 119 case 'v': return TCF_META_TYPE_VAR; 120 } 121 122 fprintf(stderr, "BUG: Unknown map character '%c'\n", k); 123 return INT_MAX; 124} 125 126static struct meta_entry * lookup_meta_entry(struct bstr *kind) 127{ 128 int i; 129 130 for (i = 0; i < (sizeof(meta_table)/sizeof(meta_table[0])); i++) 131 if (!bstrcmp(kind, meta_table[i].kind) && 132 meta_table[i].id != 0) 133 return &meta_table[i]; 134 135 return NULL; 136} 137 138static struct meta_entry * lookup_meta_entry_byid(int id) 139{ 140 int i; 141 142 for (i = 0; i < (sizeof(meta_table)/sizeof(meta_table[0])); i++) 143 if (meta_table[i].id == id) 144 return &meta_table[i]; 145 146 return NULL; 147} 148 149static inline void dump_value(struct nlmsghdr *n, int tlv, unsigned long val, 150 struct tcf_meta_val *hdr) 151{ 152 __u32 t; 153 154 switch (TCF_META_TYPE(hdr->kind)) { 155 case TCF_META_TYPE_INT: 156 t = val; 157 addattr_l(n, MAX_MSG, tlv, &t, sizeof(t)); 158 break; 159 160 case TCF_META_TYPE_VAR: 161 if (TCF_META_ID(hdr->kind) == TCF_META_ID_VALUE) { 162 struct bstr *a = (struct bstr *) val; 163 addattr_l(n, MAX_MSG, tlv, a->data, a->len); 164 } 165 break; 166 } 167} 168 169static 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 187static 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 < (sizeof(meta_table)/sizeof(meta_table[0])); 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 237static inline int can_adopt(struct tcf_meta_val *val) 238{ 239 return !!TCF_META_ID(val->kind); 240} 241 242static 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 249static inline struct bstr * 250parse_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 != LONG_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 308compatible: 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 == LONG_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 == LONG_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 354not_compatible: 355 PARSE_ERR(arg, "lvalue and rvalue are not compatible."); 356 return PARSE_FAILURE; 357} 358 359static 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 memset(&meta_hdr, 0, sizeof(meta_hdr)); 368 369 if (args == NULL) 370 return PARSE_ERR(args, "meta: missing arguments"); 371 372 if (!bstrcmp(args, "list")) { 373 list_meta_ids(stderr); 374 return -1; 375 } 376 377 a = parse_object(args, args, &meta_hdr.left, &lvalue, NULL); 378 if (a == PARSE_FAILURE) 379 return -1; 380 else if (a == NULL) 381 return PARSE_ERR(args, "meta: missing operand"); 382 383 if (!bstrcmp(a, "eq")) 384 opnd = TCF_EM_OPND_EQ; 385 else if (!bstrcmp(a, "gt")) 386 opnd = TCF_EM_OPND_GT; 387 else if (!bstrcmp(a, "lt")) 388 opnd = TCF_EM_OPND_LT; 389 else 390 return PARSE_ERR(a, "meta: invalid operand"); 391 392 meta_hdr.left.op = (__u8) opnd; 393 394 if (a->next == NULL) 395 return PARSE_ERR(args, "meta: missing rvalue"); 396 a = bstr_next(a); 397 398 a = parse_object(args, a, &meta_hdr.right, &rvalue, &meta_hdr.left); 399 if (a == PARSE_FAILURE) 400 return -1; 401 else if (a != NULL) 402 return PARSE_ERR(a, "meta: unexpected trailer"); 403 404 405 addraw_l(n, MAX_MSG, hdr, sizeof(*hdr)); 406 407 addattr_l(n, MAX_MSG, TCA_EM_META_HDR, &meta_hdr, sizeof(meta_hdr)); 408 409 if (lvalue) 410 dump_value(n, TCA_EM_META_LVALUE, lvalue, &meta_hdr.left); 411 412 if (rvalue) 413 dump_value(n, TCA_EM_META_RVALUE, rvalue, &meta_hdr.right); 414 415 return 0; 416} 417#undef PARSE_ERR 418 419static inline void print_binary(FILE *fd, unsigned char *str, int len) 420{ 421 int i; 422 423 for (i = 0; i < len; i++) 424 if (!isprint(str[i])) 425 goto binary; 426 427 for (i = 0; i < len; i++) 428 fprintf(fd, "%c", str[i]); 429 return; 430 431binary: 432 for (i = 0; i < len; i++) 433 fprintf(fd, "%02x ", str[i]); 434 435 fprintf(fd, "\""); 436 for (i = 0; i < len; i++) 437 fprintf(fd, "%c", isprint(str[i]) ? str[i] : '.'); 438 fprintf(fd, "\""); 439} 440 441static inline int print_value(FILE *fd, int type, struct rtattr *rta) 442{ 443 if (rta == NULL) { 444 fprintf(stderr, "Missing value TLV\n"); 445 return -1; 446 } 447 448 switch(type) { 449 case TCF_META_TYPE_INT: 450 if (RTA_PAYLOAD(rta) < sizeof(__u32)) { 451 fprintf(stderr, "meta int type value TLV " \ 452 "size mismatch.\n"); 453 return -1; 454 } 455 fprintf(fd, "%d", *(__u32 *) RTA_DATA(rta)); 456 break; 457 458 case TCF_META_TYPE_VAR: 459 print_binary(fd, RTA_DATA(rta), RTA_PAYLOAD(rta)); 460 break; 461 } 462 463 return 0; 464} 465 466static int print_object(FILE *fd, struct tcf_meta_val *obj, struct rtattr *rta) 467{ 468 int id = TCF_META_ID(obj->kind); 469 int type = TCF_META_TYPE(obj->kind); 470 struct meta_entry *entry; 471 472 if (id == TCF_META_ID_VALUE) 473 return print_value(fd, type, rta); 474 475 entry = lookup_meta_entry_byid(id); 476 477 if (entry == NULL) 478 fprintf(fd, "[unknown meta id %d]", id); 479 else 480 fprintf(fd, "%s", entry->kind); 481 482 if (obj->shift) 483 fprintf(fd, " shift %d", obj->shift); 484 485 switch (type) { 486 case TCF_META_TYPE_INT: 487 if (rta) { 488 if (RTA_PAYLOAD(rta) < sizeof(__u32)) 489 goto size_mismatch; 490 491 fprintf(fd, " mask 0x%08x", 492 *(__u32*) RTA_DATA(rta)); 493 } 494 break; 495 } 496 497 return 0; 498 499size_mismatch: 500 fprintf(stderr, "meta int type mask TLV size mismatch\n"); 501 return -1; 502} 503 504 505static int meta_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data, 506 int data_len) 507{ 508 struct rtattr *tb[TCA_EM_META_MAX+1]; 509 struct tcf_meta_hdr *meta_hdr; 510 511 if (parse_rtattr(tb, TCA_EM_META_MAX, data, data_len) < 0) 512 return -1; 513 514 if (tb[TCA_EM_META_HDR] == NULL) { 515 fprintf(stderr, "Missing meta header\n"); 516 return -1; 517 } 518 519 if (RTA_PAYLOAD(tb[TCA_EM_META_HDR]) < sizeof(*meta_hdr)) { 520 fprintf(stderr, "Meta header size mismatch\n"); 521 return -1; 522 } 523 524 meta_hdr = RTA_DATA(tb[TCA_EM_META_HDR]); 525 526 if (print_object(fd, &meta_hdr->left, tb[TCA_EM_META_LVALUE]) < 0) 527 return -1; 528 529 switch (meta_hdr->left.op) { 530 case TCF_EM_OPND_EQ: 531 fprintf(fd, " eq "); 532 break; 533 case TCF_EM_OPND_LT: 534 fprintf(fd, " lt "); 535 break; 536 case TCF_EM_OPND_GT: 537 fprintf(fd, " gt "); 538 break; 539 } 540 541 return print_object(fd, &meta_hdr->right, tb[TCA_EM_META_RVALUE]); 542} 543 544struct ematch_util meta_ematch_util = { 545 .kind = "meta", 546 .kind_num = TCF_EM_META, 547 .parse_eopt = meta_parse_eopt, 548 .print_eopt = meta_print_eopt, 549 .print_usage = meta_print_usage 550}; 551