1 /* 2 * em_cmp.c Simple comparison 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_cmp.h> 25 26 extern struct ematch_util cmp_ematch_util; 27 28 static void cmp_print_usage(FILE *fd) 29 { 30 fprintf(fd, 31 "Usage: cmp(ALIGN at OFFSET [ ATTRS ] { eq | lt | gt } VALUE)\n" \ 32 "where: ALIGN := { u8 | u16 | u32 }\n" \ 33 " ATTRS := [ layer LAYER ] [ mask MASK ] [ trans ]\n" \ 34 " LAYER := { link | network | transport | 0..%d }\n" \ 35 "\n" \ 36 "Example: cmp(u16 at 3 layer 2 mask 0xff00 gt 20)\n", 37 TCF_LAYER_MAX); 38 } 39 40 static int cmp_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr, 41 struct bstr *args) 42 { 43 struct bstr *a; 44 int align, opnd = 0; 45 unsigned long offset = 0, layer = TCF_LAYER_NETWORK, mask = 0, value = 0; 46 int offset_present = 0, value_present = 0; 47 struct tcf_em_cmp cmp; 48 49 memset(&cmp, 0, sizeof(cmp)); 50 51 #define PARSE_ERR(CARG, FMT, ARGS...) \ 52 em_parse_error(EINVAL, args, CARG, &cmp_ematch_util, FMT ,##ARGS) 53 54 if (args == NULL) 55 return PARSE_ERR(args, "cmp: missing arguments"); 56 57 if (!bstrcmp(args, "u8")) 58 align = TCF_EM_ALIGN_U8; 59 else if (!bstrcmp(args, "u16")) 60 align = TCF_EM_ALIGN_U16; 61 else if (!bstrcmp(args, "u32")) 62 align = TCF_EM_ALIGN_U32; 63 else 64 return PARSE_ERR(args, "cmp: invalid alignment"); 65 66 for (a = bstr_next(args); a; a = bstr_next(a)) { 67 if (!bstrcmp(a, "at")) { 68 if (a->next == NULL) 69 return PARSE_ERR(a, "cmp: missing argument"); 70 a = bstr_next(a); 71 72 offset = bstrtoul(a); 73 if (offset == ULONG_MAX) 74 return PARSE_ERR(a, "cmp: invalid offset, " \ 75 "must be numeric"); 76 77 offset_present = 1; 78 } else if (!bstrcmp(a, "layer")) { 79 if (a->next == NULL) 80 return PARSE_ERR(a, "cmp: missing argument"); 81 a = bstr_next(a); 82 83 layer = parse_layer(a); 84 if (layer == INT_MAX) { 85 layer = bstrtoul(a); 86 if (layer == ULONG_MAX) 87 return PARSE_ERR(a, "cmp: invalid " \ 88 "layer"); 89 } 90 91 if (layer > TCF_LAYER_MAX) 92 return PARSE_ERR(a, "cmp: illegal layer, " \ 93 "must be in 0..%d", TCF_LAYER_MAX); 94 } else if (!bstrcmp(a, "mask")) { 95 if (a->next == NULL) 96 return PARSE_ERR(a, "cmp: missing argument"); 97 a = bstr_next(a); 98 99 mask = bstrtoul(a); 100 if (mask == ULONG_MAX) 101 return PARSE_ERR(a, "cmp: invalid mask"); 102 } else if (!bstrcmp(a, "trans")) { 103 cmp.flags |= TCF_EM_CMP_TRANS; 104 } else if (!bstrcmp(a, "eq") || !bstrcmp(a, "gt") || 105 !bstrcmp(a, "lt")) { 106 107 if (!bstrcmp(a, "eq")) 108 opnd = TCF_EM_OPND_EQ; 109 else if (!bstrcmp(a, "gt")) 110 opnd = TCF_EM_OPND_GT; 111 else if (!bstrcmp(a, "lt")) 112 opnd = TCF_EM_OPND_LT; 113 114 if (a->next == NULL) 115 return PARSE_ERR(a, "cmp: missing argument"); 116 a = bstr_next(a); 117 118 value = bstrtoul(a); 119 if (value == ULONG_MAX) 120 return PARSE_ERR(a, "cmp: invalid value"); 121 122 value_present = 1; 123 } else 124 return PARSE_ERR(a, "nbyte: unknown parameter"); 125 } 126 127 if (offset_present == 0 || value_present == 0) 128 return PARSE_ERR(a, "cmp: offset and value required"); 129 130 cmp.val = (__u32) value; 131 cmp.mask = (__u32) mask; 132 cmp.off = (__u16) offset; 133 cmp.align = (__u8) align; 134 cmp.layer = (__u8) layer; 135 cmp.opnd = (__u8) opnd; 136 137 addraw_l(n, MAX_MSG, hdr, sizeof(*hdr)); 138 addraw_l(n, MAX_MSG, &cmp, sizeof(cmp)); 139 140 #undef PARSE_ERR 141 return 0; 142 } 143 144 static int cmp_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data, 145 int data_len) 146 { 147 struct tcf_em_cmp *cmp = data; 148 149 if (data_len < sizeof(*cmp)) { 150 fprintf(stderr, "CMP header size mismatch\n"); 151 return -1; 152 } 153 154 if (cmp->align == TCF_EM_ALIGN_U8) 155 fprintf(fd, "u8 "); 156 else if (cmp->align == TCF_EM_ALIGN_U16) 157 fprintf(fd, "u16 "); 158 else if (cmp->align == TCF_EM_ALIGN_U32) 159 fprintf(fd, "u32 "); 160 161 fprintf(fd, "at %d layer %d ", cmp->off, cmp->layer); 162 163 if (cmp->mask) 164 fprintf(fd, "mask 0x%x ", cmp->mask); 165 166 if (cmp->flags & TCF_EM_CMP_TRANS) 167 fprintf(fd, "trans "); 168 169 if (cmp->opnd == TCF_EM_OPND_EQ) 170 fprintf(fd, "eq "); 171 else if (cmp->opnd == TCF_EM_OPND_LT) 172 fprintf(fd, "lt "); 173 else if (cmp->opnd == TCF_EM_OPND_GT) 174 fprintf(fd, "gt "); 175 176 fprintf(fd, "%d", cmp->val); 177 178 return 0; 179 } 180 181 struct ematch_util cmp_ematch_util = { 182 .kind = "cmp", 183 .kind_num = TCF_EM_CMP, 184 .parse_eopt = cmp_parse_eopt, 185 .print_eopt = cmp_print_eopt, 186 .print_usage = cmp_print_usage 187 }; 188