shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 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> |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 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" \ |
Andy Furniss | a07c6d6 | 2014-09-29 20:43:01 +0100 | [diff] [blame] | 35 | "Example: meta(nf_mark gt 24)\n" \ |
Stephen Hemminger | ba26a6e | 2008-02-05 12:07:07 -0800 | [diff] [blame] | 36 | " meta(indev shift 1 eq \"ppp\")\n" \ |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 37 | " meta(tcindex mask 0xf0 eq 0xf0)\n" \ |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 38 | "\n" \ |
| 39 | "For a list of meta identifiers, use meta(list).\n"); |
| 40 | } |
| 41 | |
| 42 | struct meta_entry { |
| 43 | int id; |
Stephen Hemminger | 32a121c | 2016-03-21 11:48:36 -0700 | [diff] [blame] | 44 | char *kind; |
| 45 | char *mask; |
| 46 | char *desc; |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 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"), |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 63 | __A(SECTION, "Packet attributes", "", ""), |
| 64 | __A(PRIORITY, "priority", "i", |
| 65 | "Priority of packet"), |
| 66 | __A(PROTOCOL, "protocol", "i", |
| 67 | "Link layer protocol"), |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 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"), |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 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"), |
Stephen Hemminger | 5e76a87 | 2008-02-05 11:33:44 -0800 | [diff] [blame] | 90 | __A(VLAN_TAG, "vlan", "i", "Vlan tag"), |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 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"), |
Stephen Hemminger | 32a121c | 2016-03-21 11:48:36 -0700 | [diff] [blame] | 105 | __A(SK_WMEM_QUEUED, "sk_wmem_queue", "i", "WMEM queue"), |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 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 | |
Stephen Hemminger | 32a121c | 2016-03-21 11:48:36 -0700 | [diff] [blame] | 125 | static struct meta_entry *lookup_meta_entry(struct bstr *kind) |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 126 | { |
| 127 | int i; |
| 128 | |
Stephen Hemminger | 32a121c | 2016-03-21 11:48:36 -0700 | [diff] [blame] | 129 | for (i = 0; i < ARRAY_SIZE(meta_table); i++) |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 130 | if (!bstrcmp(kind, meta_table[i].kind) && |
| 131 | meta_table[i].id != 0) |
| 132 | return &meta_table[i]; |
Stephen Hemminger | ae665a5 | 2006-12-05 10:10:22 -0800 | [diff] [blame] | 133 | |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 134 | return NULL; |
| 135 | } |
| 136 | |
Stephen Hemminger | 32a121c | 2016-03-21 11:48:36 -0700 | [diff] [blame] | 137 | static struct meta_entry *lookup_meta_entry_byid(int id) |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 138 | { |
| 139 | int i; |
| 140 | |
Stephen Hemminger | 32a121c | 2016-03-21 11:48:36 -0700 | [diff] [blame] | 141 | for (i = 0; i < ARRAY_SIZE(meta_table); i++) |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 142 | if (meta_table[i].id == id) |
| 143 | return &meta_table[i]; |
Stephen Hemminger | ae665a5 | 2006-12-05 10:10:22 -0800 | [diff] [blame] | 144 | |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 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; |
Stephen Hemminger | 32a121c | 2016-03-21 11:48:36 -0700 | [diff] [blame] | 162 | |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 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; |
Stephen Hemminger | ae665a5 | 2006-12-05 10:10:22 -0800 | [diff] [blame] | 174 | |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 175 | entry = lookup_meta_entry_byid(TCF_META_ID(what->kind)); |
| 176 | |
| 177 | if (entry == NULL) |
| 178 | return 0; |
Stephen Hemminger | ae665a5 | 2006-12-05 10:10:22 -0800 | [diff] [blame] | 179 | |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 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 | |
Stephen Hemminger | 32a121c | 2016-03-21 11:48:36 -0700 | [diff] [blame] | 196 | for (i = 0; i < ARRAY_SIZE(meta_table); i++) { |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 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...) \ |
Stephen Hemminger | 32a121c | 2016-03-21 11:48:36 -0700 | [diff] [blame] | 235 | em_parse_error(EINVAL, args, CARG, &meta_ematch_util, FMT, ##ARGS) |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 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 | } |
Stephen Hemminger | ae665a5 | 2006-12-05 10:10:22 -0800 | [diff] [blame] | 247 | |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 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); |
Denys Fedoryshchenko | 11bbe7f | 2008-07-30 17:34:07 +0300 | [diff] [blame] | 265 | if (num != ULONG_MAX) { |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 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; |
Stephen Hemminger | ae665a5 | 2006-12-05 10:10:22 -0800 | [diff] [blame] | 283 | |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 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; |
Stephen Hemminger | ae665a5 | 2006-12-05 10:10:22 -0800 | [diff] [blame] | 304 | } else |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 305 | goto not_compatible; |
| 306 | } |
| 307 | |
| 308 | compatible: |
| 309 | |
| 310 | a = bstr_next(arg); |
| 311 | |
Stephen Hemminger | 32a121c | 2016-03-21 11:48:36 -0700 | [diff] [blame] | 312 | while (a) { |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 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); |
Stephen Hemminger | ae665a5 | 2006-12-05 10:10:22 -0800 | [diff] [blame] | 321 | |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 322 | shift = bstrtoul(a); |
Denys Fedoryshchenko | 11bbe7f | 2008-07-30 17:34:07 +0300 | [diff] [blame] | 323 | if (shift == ULONG_MAX) { |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 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); |
Stephen Hemminger | ae665a5 | 2006-12-05 10:10:22 -0800 | [diff] [blame] | 339 | |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 340 | mask = bstrtoul(a); |
Denys Fedoryshchenko | 11bbe7f | 2008-07-30 17:34:07 +0300 | [diff] [blame] | 341 | if (mask == ULONG_MAX) { |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 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; |
Phil Sutter | d17b136 | 2016-07-18 16:48:42 +0200 | [diff] [blame] | 364 | struct tcf_meta_hdr meta_hdr = {}; |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 365 | unsigned long lvalue = 0, rvalue = 0; |
| 366 | |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 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"); |
Stephen Hemminger | ae665a5 | 2006-12-05 10:10:22 -0800 | [diff] [blame] | 401 | |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 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 | |
Patrick McHardy | 6140785 | 2007-07-11 14:48:12 +0200 | [diff] [blame] | 407 | dump_value(n, TCA_EM_META_LVALUE, lvalue, &meta_hdr.left); |
| 408 | dump_value(n, TCA_EM_META_RVALUE, rvalue, &meta_hdr.right); |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 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 | |
Stephen Hemminger | 32a121c | 2016-03-21 11:48:36 -0700 | [diff] [blame] | 443 | switch (type) { |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 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 | } |
Stephen Hemminger | ff24746 | 2012-04-10 08:47:55 -0700 | [diff] [blame] | 450 | fprintf(fd, "%d", rta_getattr_u32(rta)); |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 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 | |
Phil Sutter | 247ace6 | 2016-07-14 23:10:53 +0200 | [diff] [blame] | 486 | if (rta_getattr_u32(rta)) |
| 487 | fprintf(fd, " mask 0x%08x", |
| 488 | rta_getattr_u32(rta)); |
shemminger | 311b414 | 2005-06-23 20:25:16 +0000 | [diff] [blame] | 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 | }; |