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 bool have_labelmap = !connlabel_open();
74 int tmp;
75
76 xtables_option_parse(cb);
77
78 switch (cb->entry->id) {
79 case O_LABEL:
80 if (have_labelmap)
81 tmp = nfct_labelmap_get_bit(map, cb->arg);
82 else
83 tmp = connlabel_value_parse(cb->arg);
84
85 if (tmp < 0)
86 xtables_error(PARAMETER_PROBLEM,
87 "label '%s' not found or invalid value",
88 cb->arg);
89
90 info->bit = tmp;
91 if (cb->invert)
92 info->options |= XT_CONNLABEL_OP_INVERT;
93 break;
94 case O_SET:
95 info->options |= XT_CONNLABEL_OP_SET;
96 break;
97 }
98
99 }
100
connlabel_get_name(int b)101 static const char *connlabel_get_name(int b)
102 {
103 const char *name;
104
105 if (connlabel_open())
106 return NULL;
107
108 name = nfct_labelmap_get_name(map, b);
109 if (name && strcmp(name, ""))
110 return name;
111 return NULL;
112 }
113
114 static void
connlabel_mt_print_op(const struct xt_connlabel_mtinfo * info,const char * prefix)115 connlabel_mt_print_op(const struct xt_connlabel_mtinfo *info, const char *prefix)
116 {
117 if (info->options & XT_CONNLABEL_OP_SET)
118 printf(" %sset", prefix);
119 }
120
121 static void
connlabel_mt_print(const void * ip,const struct xt_entry_match * match,int numeric)122 connlabel_mt_print(const void *ip, const struct xt_entry_match *match, int numeric)
123 {
124 const struct xt_connlabel_mtinfo *info = (const void *)match->data;
125 const char *name = connlabel_get_name(info->bit);
126
127 printf(" connlabel");
128 if (info->options & XT_CONNLABEL_OP_INVERT)
129 printf(" !");
130 if (numeric || name == NULL) {
131 printf(" %u", info->bit);
132 } else {
133 printf(" '%s'", name);
134 }
135 connlabel_mt_print_op(info, "");
136 }
137
138 static void
connlabel_mt_save(const void * ip,const struct xt_entry_match * match)139 connlabel_mt_save(const void *ip, const struct xt_entry_match *match)
140 {
141 const struct xt_connlabel_mtinfo *info = (const void *)match->data;
142 const char *name = connlabel_get_name(info->bit);
143
144 if (info->options & XT_CONNLABEL_OP_INVERT)
145 printf(" !");
146 if (name)
147 printf(" --label \"%s\"", name);
148 else
149 printf(" --label \"%u\"", info->bit);
150 connlabel_mt_print_op(info, "--");
151 }
152
connlabel_mt_xlate(struct xt_xlate * xl,const struct xt_xlate_mt_params * params)153 static int connlabel_mt_xlate(struct xt_xlate *xl,
154 const struct xt_xlate_mt_params *params)
155 {
156 const struct xt_connlabel_mtinfo *info =
157 (const void *)params->match->data;
158 const char *name = connlabel_get_name(info->bit);
159 char *valbuf = NULL;
160
161 if (name == NULL) {
162 if (asprintf(&valbuf, "%u", info->bit) < 0)
163 return 0;
164 name = valbuf;
165 }
166
167 if (info->options & XT_CONNLABEL_OP_SET)
168 xt_xlate_add(xl, "ct label set %s ", name);
169
170 xt_xlate_add(xl, "ct label ");
171 if (info->options & XT_CONNLABEL_OP_INVERT)
172 xt_xlate_add(xl, "and %s != ", name);
173 xt_xlate_add(xl, "%s", name);
174
175 free(valbuf);
176 return 1;
177 }
178
179 static struct xtables_match connlabel_mt_reg = {
180 .family = NFPROTO_UNSPEC,
181 .name = "connlabel",
182 .version = XTABLES_VERSION,
183 .size = XT_ALIGN(sizeof(struct xt_connlabel_mtinfo)),
184 .userspacesize = offsetof(struct xt_connlabel_mtinfo, bit),
185 .help = connlabel_mt_help,
186 .print = connlabel_mt_print,
187 .save = connlabel_mt_save,
188 .x6_parse = connlabel_mt_parse,
189 .x6_options = connlabel_mt_opts,
190 .xlate = connlabel_mt_xlate,
191 };
192
_init(void)193 void _init(void)
194 {
195 xtables_register_match(&connlabel_mt_reg);
196 }
197