1 #define _GNU_SOURCE
2 #include <errno.h>
3 #include <stdbool.h>
4 #include <string.h>
5 #include <stdio.h>
6 #include <stdint.h>
7 #include <stdlib.h>
8 #include <xtables.h>
9 #include <linux/netfilter/xt_connlabel.h>
10 #include <libnetfilter_conntrack/libnetfilter_conntrack.h>
11
12 enum {
13 O_LABEL = 0,
14 O_SET = 1,
15 };
16
17 static struct nfct_labelmap *map;
18
connlabel_mt_help(void)19 static void connlabel_mt_help(void)
20 {
21 puts(
22 "connlabel match options:\n"
23 "[!] --label name Match if label has been set on connection\n"
24 " --set Set label on connection");
25 }
26
27 static const struct xt_option_entry connlabel_mt_opts[] = {
28 {.name = "label", .id = O_LABEL, .type = XTTYPE_STRING,
29 .min = 1, .flags = XTOPT_MAND|XTOPT_INVERT},
30 {.name = "set", .id = O_SET, .type = XTTYPE_NONE},
31 XTOPT_TABLEEND,
32 };
33
34 /* cannot do this via _init, else static builds might spew error message
35 * for every iptables invocation.
36 */
connlabel_open(void)37 static int connlabel_open(void)
38 {
39 const char *fname;
40
41 if (map)
42 return 0;
43
44 map = nfct_labelmap_new(NULL);
45 if (map != NULL)
46 return 0;
47
48 fname = nfct_labels_get_path();
49 if (errno) {
50 fprintf(stderr, "Warning: cannot open %s: %s\n",
51 fname, strerror(errno));
52 } else {
53 xtables_error(RESOURCE_PROBLEM,
54 "cannot parse %s: no labels found", fname);
55 }
56 return 1;
57 }
58
connlabel_value_parse(const char * in)59 static int connlabel_value_parse(const char *in)
60 {
61 char *end;
62 unsigned long value = strtoul(in, &end, 0);
63
64 if (in[0] == '\0' || *end != '\0')
65 return -1;
66
67 return value;
68 }
69
connlabel_mt_parse(struct xt_option_call * cb)70 static void connlabel_mt_parse(struct xt_option_call *cb)
71 {
72 struct xt_connlabel_mtinfo *info = cb->data;
73 int tmp;
74
75 xtables_option_parse(cb);
76
77 switch (cb->entry->id) {
78 case O_LABEL:
79 tmp = connlabel_value_parse(cb->arg);
80 if (tmp < 0 && !connlabel_open())
81 tmp = nfct_labelmap_get_bit(map, cb->arg);
82 if (tmp < 0)
83 xtables_error(PARAMETER_PROBLEM,
84 "label '%s' not found or invalid value",
85 cb->arg);
86
87 info->bit = tmp;
88 if (cb->invert)
89 info->options |= XT_CONNLABEL_OP_INVERT;
90 break;
91 case O_SET:
92 info->options |= XT_CONNLABEL_OP_SET;
93 break;
94 }
95
96 }
97
connlabel_get_name(int b)98 static const char *connlabel_get_name(int b)
99 {
100 const char *name;
101
102 if (connlabel_open())
103 return NULL;
104
105 name = nfct_labelmap_get_name(map, b);
106 if (name && strcmp(name, ""))
107 return name;
108 return NULL;
109 }
110
111 static void
connlabel_mt_print_op(const struct xt_connlabel_mtinfo * info,const char * prefix)112 connlabel_mt_print_op(const struct xt_connlabel_mtinfo *info, const char *prefix)
113 {
114 if (info->options & XT_CONNLABEL_OP_SET)
115 printf(" %sset", prefix);
116 }
117
118 static void
connlabel_mt_print(const void * ip,const struct xt_entry_match * match,int numeric)119 connlabel_mt_print(const void *ip, const struct xt_entry_match *match, int numeric)
120 {
121 const struct xt_connlabel_mtinfo *info = (const void *)match->data;
122 const char *name = connlabel_get_name(info->bit);
123
124 printf(" connlabel");
125 if (info->options & XT_CONNLABEL_OP_INVERT)
126 printf(" !");
127 if (numeric || name == NULL) {
128 printf(" %u", info->bit);
129 } else {
130 printf(" '%s'", name);
131 }
132 connlabel_mt_print_op(info, "");
133 }
134
135 static void
connlabel_mt_save(const void * ip,const struct xt_entry_match * match)136 connlabel_mt_save(const void *ip, const struct xt_entry_match *match)
137 {
138 const struct xt_connlabel_mtinfo *info = (const void *)match->data;
139 const char *name = connlabel_get_name(info->bit);
140
141 if (info->options & XT_CONNLABEL_OP_INVERT)
142 printf(" !");
143 if (name)
144 printf(" --label \"%s\"", name);
145 else
146 printf(" --label \"%u\"", info->bit);
147 connlabel_mt_print_op(info, "--");
148 }
149
connlabel_mt_xlate(struct xt_xlate * xl,const struct xt_xlate_mt_params * params)150 static int connlabel_mt_xlate(struct xt_xlate *xl,
151 const struct xt_xlate_mt_params *params)
152 {
153 const struct xt_connlabel_mtinfo *info =
154 (const void *)params->match->data;
155 const char *name = connlabel_get_name(info->bit);
156 char *valbuf = NULL;
157
158 if (name == NULL) {
159 if (asprintf(&valbuf, "%u", info->bit) < 0)
160 return 0;
161 name = valbuf;
162 }
163
164 if (info->options & XT_CONNLABEL_OP_SET)
165 xt_xlate_add(xl, "ct label set %s ", name);
166
167 xt_xlate_add(xl, "ct label ");
168 if (info->options & XT_CONNLABEL_OP_INVERT)
169 xt_xlate_add(xl, "and %s != ", name);
170 xt_xlate_add(xl, "%s", name);
171
172 free(valbuf);
173 return 1;
174 }
175
176 static struct xtables_match connlabel_mt_reg = {
177 .family = NFPROTO_UNSPEC,
178 .name = "connlabel",
179 .version = XTABLES_VERSION,
180 .size = XT_ALIGN(sizeof(struct xt_connlabel_mtinfo)),
181 .userspacesize = offsetof(struct xt_connlabel_mtinfo, bit),
182 .help = connlabel_mt_help,
183 .print = connlabel_mt_print,
184 .save = connlabel_mt_save,
185 .x6_parse = connlabel_mt_parse,
186 .x6_options = connlabel_mt_opts,
187 .xlate = connlabel_mt_xlate,
188 };
189
_init(void)190 void _init(void)
191 {
192 xtables_register_match(&connlabel_mt_reg);
193 }
194