b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame^] | 1 | From 74267bacce0c43e5038b0377cb7c08f1ad9d50a3 Mon Sep 17 00:00:00 2001 |
| 2 | From: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk> |
| 3 | Date: Sat, 23 Mar 2019 10:21:03 +0000 |
| 4 | Subject: [PATCH] iptables: connmark - add set-dscpmark option for openwrt |
| 5 | |
| 6 | Naive user space front end to xt_connmark 'setdscp' option. |
| 7 | |
| 8 | iptables -A QOS_MARK_eth0 -t mangle -j CONNMARK --set-dscpmark 0xfc000000/0x01000000 |
| 9 | |
| 10 | This version has a hack to support a backport to 4.14 |
| 11 | |
| 12 | Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk> |
| 13 | --- |
| 14 | extensions/libxt_CONNMARK.c | 315 +++++++++++++++++++++++++- |
| 15 | include/linux/netfilter/xt_connmark.h | 10 + |
| 16 | 2 files changed, 324 insertions(+), 1 deletion(-) |
| 17 | |
| 18 | --- a/extensions/libxt_CONNMARK.c |
| 19 | +++ b/extensions/libxt_CONNMARK.c |
| 20 | @@ -22,6 +22,7 @@ |
| 21 | #include <stdbool.h> |
| 22 | #include <stdint.h> |
| 23 | #include <stdio.h> |
| 24 | +#include <strings.h> |
| 25 | #include <xtables.h> |
| 26 | #include <linux/netfilter/xt_CONNMARK.h> |
| 27 | |
| 28 | @@ -49,6 +50,7 @@ enum { |
| 29 | O_CTMASK, |
| 30 | O_NFMASK, |
| 31 | O_MASK, |
| 32 | + O_DSCP_MARK, |
| 33 | F_SET_MARK = 1 << O_SET_MARK, |
| 34 | F_SAVE_MARK = 1 << O_SAVE_MARK, |
| 35 | F_RESTORE_MARK = 1 << O_RESTORE_MARK, |
| 36 | @@ -61,8 +63,10 @@ enum { |
| 37 | F_CTMASK = 1 << O_CTMASK, |
| 38 | F_NFMASK = 1 << O_NFMASK, |
| 39 | F_MASK = 1 << O_MASK, |
| 40 | + F_DSCP_MARK = 1 << O_DSCP_MARK, |
| 41 | F_OP_ANY = F_SET_MARK | F_SAVE_MARK | F_RESTORE_MARK | |
| 42 | - F_AND_MARK | F_OR_MARK | F_XOR_MARK | F_SET_XMARK, |
| 43 | + F_AND_MARK | F_OR_MARK | F_XOR_MARK | F_SET_XMARK | |
| 44 | + F_DSCP_MARK, |
| 45 | }; |
| 46 | |
| 47 | static const char *const xt_connmark_shift_ops[] = { |
| 48 | @@ -114,6 +118,8 @@ static const struct xt_option_entry conn |
| 49 | .excl = F_MASK, .flags = XTOPT_PUT, XTOPT_POINTER(s, nfmask)}, |
| 50 | {.name = "mask", .id = O_MASK, .type = XTTYPE_UINT32, |
| 51 | .excl = F_CTMASK | F_NFMASK}, |
| 52 | + {.name = "set-dscpmark", .id = O_DSCP_MARK, .type = XTTYPE_MARKMASK32, |
| 53 | + .excl = F_OP_ANY}, |
| 54 | XTOPT_TABLEEND, |
| 55 | }; |
| 56 | #undef s |
| 57 | @@ -148,6 +154,38 @@ static const struct xt_option_entry conn |
| 58 | }; |
| 59 | #undef s |
| 60 | |
| 61 | +#define s struct xt_connmark_tginfo3 |
| 62 | +static const struct xt_option_entry connmark_tg_opts_v3[] = { |
| 63 | + {.name = "set-xmark", .id = O_SET_XMARK, .type = XTTYPE_MARKMASK32, |
| 64 | + .excl = F_OP_ANY}, |
| 65 | + {.name = "set-mark", .id = O_SET_MARK, .type = XTTYPE_MARKMASK32, |
| 66 | + .excl = F_OP_ANY}, |
| 67 | + {.name = "and-mark", .id = O_AND_MARK, .type = XTTYPE_UINT32, |
| 68 | + .excl = F_OP_ANY}, |
| 69 | + {.name = "or-mark", .id = O_OR_MARK, .type = XTTYPE_UINT32, |
| 70 | + .excl = F_OP_ANY}, |
| 71 | + {.name = "xor-mark", .id = O_XOR_MARK, .type = XTTYPE_UINT32, |
| 72 | + .excl = F_OP_ANY}, |
| 73 | + {.name = "save-mark", .id = O_SAVE_MARK, .type = XTTYPE_NONE, |
| 74 | + .excl = F_OP_ANY}, |
| 75 | + {.name = "restore-mark", .id = O_RESTORE_MARK, .type = XTTYPE_NONE, |
| 76 | + .excl = F_OP_ANY}, |
| 77 | + {.name = "left-shift-mark", .id = O_LEFT_SHIFT_MARK, .type = XTTYPE_UINT8, |
| 78 | + .min = 0, .max = 32}, |
| 79 | + {.name = "right-shift-mark", .id = O_RIGHT_SHIFT_MARK, .type = XTTYPE_UINT8, |
| 80 | + .min = 0, .max = 32}, |
| 81 | + {.name = "ctmask", .id = O_CTMASK, .type = XTTYPE_UINT32, |
| 82 | + .excl = F_MASK, .flags = XTOPT_PUT, XTOPT_POINTER(s, ctmask)}, |
| 83 | + {.name = "nfmask", .id = O_NFMASK, .type = XTTYPE_UINT32, |
| 84 | + .excl = F_MASK, .flags = XTOPT_PUT, XTOPT_POINTER(s, nfmask)}, |
| 85 | + {.name = "mask", .id = O_MASK, .type = XTTYPE_UINT32, |
| 86 | + .excl = F_CTMASK | F_NFMASK}, |
| 87 | + {.name = "set-dscpmark", .id = O_DSCP_MARK, .type = XTTYPE_MARKMASK32, |
| 88 | + .excl = F_OP_ANY}, |
| 89 | + XTOPT_TABLEEND, |
| 90 | +}; |
| 91 | +#undef s |
| 92 | + |
| 93 | static void connmark_tg_help(void) |
| 94 | { |
| 95 | printf( |
| 96 | @@ -175,6 +213,15 @@ static void connmark_tg_help_v2(void) |
| 97 | ); |
| 98 | } |
| 99 | |
| 100 | +static void connmark_tg_help_v3(void) |
| 101 | +{ |
| 102 | + connmark_tg_help_v2(); |
| 103 | + printf( |
| 104 | +" --set-dscpmark value/mask Save DSCP to conntrack mark value\n" |
| 105 | +); |
| 106 | +} |
| 107 | + |
| 108 | + |
| 109 | static void connmark_tg_init(struct xt_entry_target *target) |
| 110 | { |
| 111 | struct xt_connmark_tginfo1 *info = (void *)target->data; |
| 112 | @@ -199,6 +246,16 @@ static void connmark_tg_init_v2(struct x |
| 113 | info->shift_bits = 0; |
| 114 | } |
| 115 | |
| 116 | +static void connmark_tg_init_v3(struct xt_entry_target *target) |
| 117 | +{ |
| 118 | + struct xt_connmark_tginfo3 *info; |
| 119 | + |
| 120 | + connmark_tg_init_v2(target); |
| 121 | + info = (void *)target->data; |
| 122 | + |
| 123 | + info->func = 0; |
| 124 | +} |
| 125 | + |
| 126 | static void CONNMARK_parse(struct xt_option_call *cb) |
| 127 | { |
| 128 | struct xt_connmark_target_info *markinfo = cb->data; |
| 129 | @@ -253,6 +310,23 @@ static void connmark_tg_parse(struct xt_ |
| 130 | info->ctmark = cb->val.u32; |
| 131 | info->ctmask = 0; |
| 132 | break; |
| 133 | + case O_DSCP_MARK: |
| 134 | +/* we sneaky sneaky this. nfmask isn't used by the set mark functionality |
| 135 | + * and by default is set to uint32max. We can use the top bit as a flag |
| 136 | + * that we're in DSCP_MARK submode of SET_MARK, if set then it's normal |
| 137 | + * if unset then we're in DSCP_MARK |
| 138 | + */ |
| 139 | + info->mode = XT_CONNMARK_SET; |
| 140 | + info->ctmark = cb->val.mark; |
| 141 | + info->ctmask = cb->val.mask; |
| 142 | + info->nfmask = info->ctmark ? ffs(info->ctmark) - 1 : 0; |
| 143 | + /* need 6 contiguous bits */ |
| 144 | + if ((~0 & (info->ctmark >> info->nfmask)) != 0x3f) |
| 145 | + xtables_error(PARAMETER_PROBLEM, |
| 146 | + "CONNMARK set-dscpmark: need 6 contiguous dscpmask bits"); |
| 147 | + if (info->ctmark & info->ctmask) |
| 148 | + xtables_error(PARAMETER_PROBLEM, |
| 149 | + "CONNMARK set-dscpmark: dscpmask/statemask bits overlap"); |
| 150 | case O_SAVE_MARK: |
| 151 | info->mode = XT_CONNMARK_SAVE; |
| 152 | break; |
| 153 | @@ -320,6 +394,78 @@ static void connmark_tg_parse_v2(struct |
| 154 | } |
| 155 | } |
| 156 | |
| 157 | +static void connmark_tg_parse_v3(struct xt_option_call *cb) |
| 158 | +{ |
| 159 | + struct xt_connmark_tginfo3 *info = cb->data; |
| 160 | + |
| 161 | + xtables_option_parse(cb); |
| 162 | + switch (cb->entry->id) { |
| 163 | + case O_SET_XMARK: |
| 164 | + info->mode = XT_CONNMARK_SET; |
| 165 | + info->func = XT_CONNMARK_VALUE; |
| 166 | + info->ctmark = cb->val.mark; |
| 167 | + info->ctmask = cb->val.mask; |
| 168 | + break; |
| 169 | + case O_SET_MARK: |
| 170 | + info->mode = XT_CONNMARK_SET; |
| 171 | + info->func = XT_CONNMARK_VALUE; |
| 172 | + info->ctmark = cb->val.mark; |
| 173 | + info->ctmask = cb->val.mark | cb->val.mask; |
| 174 | + break; |
| 175 | + case O_AND_MARK: |
| 176 | + info->mode = XT_CONNMARK_SET; |
| 177 | + info->func = XT_CONNMARK_VALUE; |
| 178 | + info->ctmark = 0; |
| 179 | + info->ctmask = ~cb->val.u32; |
| 180 | + break; |
| 181 | + case O_OR_MARK: |
| 182 | + info->mode = XT_CONNMARK_SET; |
| 183 | + info->func = XT_CONNMARK_VALUE; |
| 184 | + info->ctmark = cb->val.u32; |
| 185 | + info->ctmask = cb->val.u32; |
| 186 | + break; |
| 187 | + case O_XOR_MARK: |
| 188 | + info->mode = XT_CONNMARK_SET; |
| 189 | + info->func = XT_CONNMARK_VALUE; |
| 190 | + info->ctmark = cb->val.u32; |
| 191 | + info->ctmask = 0; |
| 192 | + break; |
| 193 | + case O_DSCP_MARK: |
| 194 | + info->mode = XT_CONNMARK_SET; |
| 195 | + info->func = XT_CONNMARK_DSCP; |
| 196 | + info->ctmark = cb->val.mark; |
| 197 | + info->ctmask = cb->val.mask; |
| 198 | + info->shift_bits = info->ctmark ? ffs(info->ctmark) - 1 : 0; |
| 199 | + /* need 6 contiguous bits */ |
| 200 | + if ((~0 & (info->ctmark >> info->shift_bits)) != 0x3f) |
| 201 | + xtables_error(PARAMETER_PROBLEM, |
| 202 | + "CONNMARK set-dscpmark: need 6 contiguous dscpmask bits"); |
| 203 | + if (info->ctmark & info->ctmask) |
| 204 | + xtables_error(PARAMETER_PROBLEM, |
| 205 | + "CONNMARK set-dscpmark: dscpmask/statemask bits overlap"); |
| 206 | + break; |
| 207 | + case O_SAVE_MARK: |
| 208 | + info->mode = XT_CONNMARK_SAVE; |
| 209 | + break; |
| 210 | + case O_RESTORE_MARK: |
| 211 | + info->mode = XT_CONNMARK_RESTORE; |
| 212 | + break; |
| 213 | + case O_MASK: |
| 214 | + info->nfmask = info->ctmask = cb->val.u32; |
| 215 | + break; |
| 216 | + case O_LEFT_SHIFT_MARK: |
| 217 | + info->shift_dir = D_SHIFT_LEFT; |
| 218 | + info->shift_bits = cb->val.u8; |
| 219 | + break; |
| 220 | + case O_RIGHT_SHIFT_MARK: |
| 221 | + info->shift_dir = D_SHIFT_RIGHT; |
| 222 | + info->shift_bits = cb->val.u8; |
| 223 | + break; |
| 224 | + default: |
| 225 | + break; |
| 226 | + } |
| 227 | +} |
| 228 | + |
| 229 | static void connmark_tg_check(struct xt_fcheck_call *cb) |
| 230 | { |
| 231 | if (!(cb->xflags & F_OP_ANY)) |
| 232 | @@ -463,6 +609,65 @@ connmark_tg_print_v2(const void *ip, con |
| 233 | } |
| 234 | } |
| 235 | |
| 236 | +static void |
| 237 | +connmark_tg_print_v3(const void *ip, const struct xt_entry_target *target, |
| 238 | + int numeric) |
| 239 | +{ |
| 240 | + const struct xt_connmark_tginfo3 *info = (const void *)target->data; |
| 241 | + const char *shift_op = xt_connmark_shift_ops[info->shift_dir]; |
| 242 | + |
| 243 | + switch (info->mode) { |
| 244 | + case XT_CONNMARK_SET: |
| 245 | + if (info->func & XT_CONNMARK_DSCP) { |
| 246 | + printf(" CONNMARK DSCP 0x%x/0x%x", |
| 247 | + info->ctmark, info->ctmask); |
| 248 | + } |
| 249 | + if (info->func & XT_CONNMARK_VALUE) { |
| 250 | + if (info->ctmark == 0) |
| 251 | + printf(" CONNMARK and 0x%x", |
| 252 | + (unsigned int)(uint32_t)~info->ctmask); |
| 253 | + else if (info->ctmark == info->ctmask) |
| 254 | + printf(" CONNMARK or 0x%x", info->ctmark); |
| 255 | + else if (info->ctmask == 0) |
| 256 | + printf(" CONNMARK xor 0x%x", info->ctmark); |
| 257 | + else if (info->ctmask == 0xFFFFFFFFU) |
| 258 | + printf(" CONNMARK set 0x%x", info->ctmark); |
| 259 | + else |
| 260 | + printf(" CONNMARK xset 0x%x/0x%x", |
| 261 | + info->ctmark, info->ctmask); |
| 262 | + } |
| 263 | + break; |
| 264 | + case XT_CONNMARK_SAVE: |
| 265 | + if (info->nfmask == UINT32_MAX && info->ctmask == UINT32_MAX) |
| 266 | + printf(" CONNMARK save"); |
| 267 | + else if (info->nfmask == info->ctmask) |
| 268 | + printf(" CONNMARK save mask 0x%x", info->nfmask); |
| 269 | + else |
| 270 | + printf(" CONNMARK save nfmask 0x%x ctmask ~0x%x", |
| 271 | + info->nfmask, info->ctmask); |
| 272 | + break; |
| 273 | + case XT_CONNMARK_RESTORE: |
| 274 | + if (info->ctmask == UINT32_MAX && info->nfmask == UINT32_MAX) |
| 275 | + printf(" CONNMARK restore"); |
| 276 | + else if (info->ctmask == info->nfmask) |
| 277 | + printf(" CONNMARK restore mask 0x%x", info->ctmask); |
| 278 | + else |
| 279 | + printf(" CONNMARK restore ctmask 0x%x nfmask ~0x%x", |
| 280 | + info->ctmask, info->nfmask); |
| 281 | + break; |
| 282 | + |
| 283 | + default: |
| 284 | + printf(" ERROR: UNKNOWN CONNMARK MODE"); |
| 285 | + break; |
| 286 | + } |
| 287 | + |
| 288 | + if (info->mode <= XT_CONNMARK_RESTORE && |
| 289 | + !(info->mode == XT_CONNMARK_SET && info->func == XT_CONNMARK_DSCP) && |
| 290 | + info->shift_bits != 0) { |
| 291 | + printf(" %s %u", shift_op, info->shift_bits); |
| 292 | + } |
| 293 | +} |
| 294 | + |
| 295 | static void CONNMARK_save(const void *ip, const struct xt_entry_target *target) |
| 296 | { |
| 297 | const struct xt_connmark_target_info *markinfo = |
| 298 | @@ -548,6 +753,38 @@ connmark_tg_save_v2(const void *ip, cons |
| 299 | } |
| 300 | } |
| 301 | |
| 302 | +static void |
| 303 | +connmark_tg_save_v3(const void *ip, const struct xt_entry_target *target) |
| 304 | +{ |
| 305 | + const struct xt_connmark_tginfo3 *info = (const void *)target->data; |
| 306 | + const char *shift_op = xt_connmark_shift_ops[info->shift_dir]; |
| 307 | + |
| 308 | + switch (info->mode) { |
| 309 | + case XT_CONNMARK_SET: |
| 310 | + if (info->func & XT_CONNMARK_VALUE) |
| 311 | + printf(" --set-xmark 0x%x/0x%x", info->ctmark, info->ctmask); |
| 312 | + if (info->func & XT_CONNMARK_DSCP) |
| 313 | + printf(" --set-dscpmark 0x%x/0x%x", info->ctmark, info->ctmask); |
| 314 | + break; |
| 315 | + case XT_CONNMARK_SAVE: |
| 316 | + printf(" --save-mark --nfmask 0x%x --ctmask 0x%x", |
| 317 | + info->nfmask, info->ctmask); |
| 318 | + break; |
| 319 | + case XT_CONNMARK_RESTORE: |
| 320 | + printf(" --restore-mark --nfmask 0x%x --ctmask 0x%x", |
| 321 | + info->nfmask, info->ctmask); |
| 322 | + break; |
| 323 | + default: |
| 324 | + printf(" ERROR: UNKNOWN CONNMARK MODE"); |
| 325 | + break; |
| 326 | + } |
| 327 | + if (info->mode <= XT_CONNMARK_RESTORE && |
| 328 | + !(info->mode == XT_CONNMARK_SET && info->func == XT_CONNMARK_DSCP) && |
| 329 | + info->shift_bits != 0) { |
| 330 | + printf(" --%s %u", shift_op, info->shift_bits); |
| 331 | + } |
| 332 | +} |
| 333 | + |
| 334 | static int connmark_tg_xlate(struct xt_xlate *xl, |
| 335 | const struct xt_xlate_tg_params *params) |
| 336 | { |
| 337 | @@ -639,6 +876,66 @@ static int connmark_tg_xlate_v2(struct x |
| 338 | |
| 339 | return 1; |
| 340 | } |
| 341 | + |
| 342 | +static int connmark_tg_xlate_v3(struct xt_xlate *xl, |
| 343 | + const struct xt_xlate_tg_params *params) |
| 344 | +{ |
| 345 | + const struct xt_connmark_tginfo3 *info = |
| 346 | + (const void *)params->target->data; |
| 347 | + const char *shift_op = xt_connmark_shift_ops[info->shift_dir]; |
| 348 | + |
| 349 | + switch (info->mode) { |
| 350 | + case XT_CONNMARK_SET: |
| 351 | + xt_xlate_add(xl, "ct mark set "); |
| 352 | + if (info->func & XT_CONNMARK_VALUE) { |
| 353 | + if (info->ctmask == 0xFFFFFFFFU) |
| 354 | + xt_xlate_add(xl, "0x%x ", info->ctmark); |
| 355 | + else if (info->ctmark == 0) |
| 356 | + xt_xlate_add(xl, "ct mark and 0x%x", ~info->ctmask); |
| 357 | + else if (info->ctmark == info->ctmask) |
| 358 | + xt_xlate_add(xl, "ct mark or 0x%x", |
| 359 | + info->ctmark); |
| 360 | + else if (info->ctmask == 0) |
| 361 | + xt_xlate_add(xl, "ct mark xor 0x%x", |
| 362 | + info->ctmark); |
| 363 | + else |
| 364 | + xt_xlate_add(xl, "ct mark xor 0x%x and 0x%x", |
| 365 | + info->ctmark, ~info->ctmask); |
| 366 | + } |
| 367 | + if (info->func & XT_CONNMARK_DSCP) { |
| 368 | +/* FIXME the nftables syntax would go here if only we knew what it was */ |
| 369 | + xt_xlate_add(xl, "ct mark set typeof(ct mark) ip dscp " |
| 370 | + "<< %u or 0x%x", info->shift_bits, |
| 371 | + info->ctmask); |
| 372 | + } |
| 373 | + break; |
| 374 | + case XT_CONNMARK_SAVE: |
| 375 | + xt_xlate_add(xl, "ct mark set mark"); |
| 376 | + if (!(info->nfmask == UINT32_MAX && |
| 377 | + info->ctmask == UINT32_MAX)) { |
| 378 | + if (info->nfmask == info->ctmask) |
| 379 | + xt_xlate_add(xl, " and 0x%x", info->nfmask); |
| 380 | + } |
| 381 | + break; |
| 382 | + case XT_CONNMARK_RESTORE: |
| 383 | + xt_xlate_add(xl, "meta mark set ct mark"); |
| 384 | + if (!(info->nfmask == UINT32_MAX && |
| 385 | + info->ctmask == UINT32_MAX)) { |
| 386 | + if (info->nfmask == info->ctmask) |
| 387 | + xt_xlate_add(xl, " and 0x%x", info->nfmask); |
| 388 | + } |
| 389 | + break; |
| 390 | + } |
| 391 | + |
| 392 | + if (info->mode <= XT_CONNMARK_RESTORE && |
| 393 | + !(info->mode == XT_CONNMARK_SET && info->func == XT_CONNMARK_DSCP) && |
| 394 | + info->shift_bits != 0) { |
| 395 | + xt_xlate_add(xl, " %s %u", shift_op, info->shift_bits); |
| 396 | + } |
| 397 | + |
| 398 | + return 1; |
| 399 | +} |
| 400 | + |
| 401 | static struct xtables_target connmark_tg_reg[] = { |
| 402 | { |
| 403 | .family = NFPROTO_UNSPEC, |
| 404 | @@ -687,6 +984,22 @@ static struct xtables_target connmark_tg |
| 405 | .x6_options = connmark_tg_opts_v2, |
| 406 | .xlate = connmark_tg_xlate_v2, |
| 407 | }, |
| 408 | + { |
| 409 | + .version = XTABLES_VERSION, |
| 410 | + .name = "CONNMARK", |
| 411 | + .revision = 3, |
| 412 | + .family = NFPROTO_UNSPEC, |
| 413 | + .size = XT_ALIGN(sizeof(struct xt_connmark_tginfo3)), |
| 414 | + .userspacesize = XT_ALIGN(sizeof(struct xt_connmark_tginfo3)), |
| 415 | + .help = connmark_tg_help_v3, |
| 416 | + .init = connmark_tg_init_v3, |
| 417 | + .print = connmark_tg_print_v3, |
| 418 | + .save = connmark_tg_save_v3, |
| 419 | + .x6_parse = connmark_tg_parse_v3, |
| 420 | + .x6_fcheck = connmark_tg_check, |
| 421 | + .x6_options = connmark_tg_opts_v3, |
| 422 | + .xlate = connmark_tg_xlate_v3, |
| 423 | + }, |
| 424 | }; |
| 425 | |
| 426 | void _init(void) |
| 427 | --- a/include/linux/netfilter/xt_connmark.h |
| 428 | +++ b/include/linux/netfilter/xt_connmark.h |
| 429 | @@ -18,6 +18,11 @@ enum { |
| 430 | XT_CONNMARK_RESTORE |
| 431 | }; |
| 432 | |
| 433 | +enum { |
| 434 | + XT_CONNMARK_VALUE = (1 << 0), |
| 435 | + XT_CONNMARK_DSCP = (1 << 1) |
| 436 | +}; |
| 437 | + |
| 438 | struct xt_connmark_tginfo1 { |
| 439 | __u32 ctmark, ctmask, nfmask; |
| 440 | __u8 mode; |
| 441 | @@ -28,6 +33,11 @@ struct xt_connmark_tginfo2 { |
| 442 | __u8 shift_dir, shift_bits, mode; |
| 443 | }; |
| 444 | |
| 445 | +struct xt_connmark_tginfo3 { |
| 446 | + __u32 ctmark, ctmask, nfmask; |
| 447 | + __u8 shift_dir, shift_bits, mode, func; |
| 448 | +}; |
| 449 | + |
| 450 | struct xt_connmark_mtinfo1 { |
| 451 | __u32 mark, mask; |
| 452 | __u8 invert; |