Annotation of embedaddon/bird2/lib/flowspec.c, revision 1.1
1.1 ! misho 1: /*
! 2: * BIRD Library -- Flow specification (RFC 5575)
! 3: *
! 4: * (c) 2016 CZ.NIC z.s.p.o.
! 5: *
! 6: * Can be freely distributed and used under the terms of the GNU GPL.
! 7: */
! 8:
! 9: /**
! 10: * DOC: Flow specification (flowspec)
! 11: *
! 12: * Flowspec are rules (RFC 5575) for firewalls disseminated using BGP protocol.
! 13: * The |flowspec.c| is a library for handling flowspec binary streams and
! 14: * flowspec data structures. You will find there functions for validation
! 15: * incoming flowspec binary streams, iterators for jumping over components,
! 16: * functions for handling a length and functions for formatting flowspec data
! 17: * structure into user-friendly text representation.
! 18: *
! 19: * In this library, you will find also flowspec builder. In |confbase.Y|, there
! 20: * are grammar's rules for parsing and building new flowspec data structure
! 21: * from BIRD's configuration files and from BIRD's command line interface.
! 22: * Finalize function will assemble final &net_addr_flow4 or &net_addr_flow6
! 23: * data structure.
! 24: *
! 25: * The data structures &net_addr_flow4 and &net_addr_flow6 are defined in
! 26: * |net.h| file. The attribute length is size of whole data structure plus
! 27: * binary stream representation of flowspec including a compressed encoded
! 28: * length of flowspec.
! 29: *
! 30: * Sometimes in code, it is used expression flowspec type, it should mean
! 31: * flowspec component type.
! 32: */
! 33:
! 34: #include "nest/bird.h"
! 35: #include "lib/flowspec.h"
! 36: #include "conf/conf.h"
! 37:
! 38:
! 39: static const char* flow4_type_str[] = {
! 40: [FLOW_TYPE_DST_PREFIX] = "dst",
! 41: [FLOW_TYPE_SRC_PREFIX] = "src",
! 42: [FLOW_TYPE_IP_PROTOCOL] = "proto",
! 43: [FLOW_TYPE_PORT] = "port",
! 44: [FLOW_TYPE_DST_PORT] = "dport",
! 45: [FLOW_TYPE_SRC_PORT] = "sport",
! 46: [FLOW_TYPE_ICMP_TYPE] = "icmp type",
! 47: [FLOW_TYPE_ICMP_CODE] = "icmp code",
! 48: [FLOW_TYPE_TCP_FLAGS] = "tcp flags",
! 49: [FLOW_TYPE_PACKET_LENGTH] = "length",
! 50: [FLOW_TYPE_DSCP] = "dscp",
! 51: [FLOW_TYPE_FRAGMENT] = "fragment"
! 52: };
! 53:
! 54: static const char* flow6_type_str[] = {
! 55: [FLOW_TYPE_DST_PREFIX] = "dst",
! 56: [FLOW_TYPE_SRC_PREFIX] = "src",
! 57: [FLOW_TYPE_NEXT_HEADER] = "next header",
! 58: [FLOW_TYPE_PORT] = "port",
! 59: [FLOW_TYPE_DST_PORT] = "dport",
! 60: [FLOW_TYPE_SRC_PORT] = "sport",
! 61: [FLOW_TYPE_ICMP_TYPE] = "icmp type",
! 62: [FLOW_TYPE_ICMP_CODE] = "icmp code",
! 63: [FLOW_TYPE_TCP_FLAGS] = "tcp flags",
! 64: [FLOW_TYPE_PACKET_LENGTH] = "length",
! 65: [FLOW_TYPE_DSCP] = "dscp",
! 66: [FLOW_TYPE_FRAGMENT] = "fragment",
! 67: [FLOW_TYPE_LABEL] = "label"
! 68: };
! 69:
! 70: /**
! 71: * flow_type_str - get stringified flowspec name of component
! 72: * @type: flowspec component type
! 73: * @ipv6: IPv4/IPv6 decide flag, use zero for IPv4 and one for IPv6
! 74: *
! 75: * This function returns flowspec name of component @type in string.
! 76: */
! 77: const char *
! 78: flow_type_str(enum flow_type type, int ipv6)
! 79: {
! 80: return ipv6 ? flow6_type_str[type] : flow4_type_str[type];
! 81: }
! 82:
! 83: /*
! 84: * Length
! 85: */
! 86:
! 87: /**
! 88: * flow_write_length - write compressed length value
! 89: * @data: destination buffer to write
! 90: * @len: the value of the length (0 to 0xfff) for writing
! 91: *
! 92: * This function writes appropriate as (1- or 2-bytes) the value of @len into
! 93: * buffer @data. The function returns number of written bytes, thus 1 or 2 bytes.
! 94: */
! 95: uint
! 96: flow_write_length(byte *data, u16 len)
! 97: {
! 98: if (len >= 0xf0)
! 99: {
! 100: put_u16(data, len | 0xf000);
! 101: return 2;
! 102: }
! 103:
! 104: *data = len;
! 105: return 1;
! 106: }
! 107:
! 108: inline static uint
! 109: get_value_length(const byte *op)
! 110: {
! 111: return (1 << ((*op & 0x30) >> 4));
! 112: }
! 113:
! 114:
! 115:
! 116: /*
! 117: * Flowspec iterators
! 118: */
! 119:
! 120: static inline u8 num_op(const byte *op) { return (*op & 0x07); }
! 121: static inline int isset_and(const byte *op) { return ((*op & 0x40) == 0x40); }
! 122: static inline int isset_end(const byte *op) { return ((*op & 0x80) == 0x80); }
! 123:
! 124: static const byte *
! 125: flow_first_part(const byte *data)
! 126: {
! 127: if (!data || flow_read_length(data) == 0)
! 128: return NULL;
! 129:
! 130: /* It is allowed to encode the value of length less then 240 into 2-bytes too */
! 131: if ((data[0] & 0xf0) == 0xf0)
! 132: return data + 2;
! 133:
! 134: return data + 1;
! 135: }
! 136:
! 137: /**
! 138: * flow4_first_part - get position of the first flowspec component
! 139: * @f: flowspec data structure &net_addr_flow4
! 140: *
! 141: * This function return a position to the beginning of the first flowspec
! 142: * component in IPv4 flowspec @f.
! 143: */
! 144: inline const byte *
! 145: flow4_first_part(const net_addr_flow4 *f)
! 146: {
! 147: return f ? flow_first_part(f->data) : NULL;
! 148: }
! 149:
! 150: /**
! 151: * flow6_first_part - get position of the first flowspec component
! 152: * @f: flowspec data structure &net_addr_flow6
! 153: *
! 154: * This function return a position to the beginning of the first flowspec
! 155: * component in IPv6 flowspec @f.
! 156: */
! 157: inline const byte *
! 158: flow6_first_part(const net_addr_flow6 *f)
! 159: {
! 160: return f ? flow_first_part(f->data) : NULL;
! 161: }
! 162:
! 163: static const byte *
! 164: flow_next_part(const byte *pos, const byte *end, int ipv6)
! 165: {
! 166: switch (*pos++)
! 167: {
! 168: case FLOW_TYPE_DST_PREFIX:
! 169: case FLOW_TYPE_SRC_PREFIX:
! 170: {
! 171: uint pxlen = *pos++;
! 172: uint bytes = BYTES(pxlen);
! 173: if (ipv6)
! 174: {
! 175: uint offset = *pos++ / 8;
! 176: pos += bytes - offset;
! 177: }
! 178: else
! 179: {
! 180: pos += bytes;
! 181: }
! 182: break;
! 183: }
! 184:
! 185: case FLOW_TYPE_IP_PROTOCOL: /* == FLOW_TYPE_NEXT_HEADER */
! 186: case FLOW_TYPE_PORT:
! 187: case FLOW_TYPE_DST_PORT:
! 188: case FLOW_TYPE_SRC_PORT:
! 189: case FLOW_TYPE_ICMP_TYPE:
! 190: case FLOW_TYPE_ICMP_CODE:
! 191: case FLOW_TYPE_TCP_FLAGS:
! 192: case FLOW_TYPE_PACKET_LENGTH:
! 193: case FLOW_TYPE_DSCP:
! 194: case FLOW_TYPE_FRAGMENT:
! 195: case FLOW_TYPE_LABEL:
! 196: {
! 197: /* Is this the end of list operator-value pair? */
! 198: uint last = 0;
! 199:
! 200: while (!last)
! 201: {
! 202: last = isset_end(pos);
! 203:
! 204: /* Value length of operator */
! 205: uint len = get_value_length(pos);
! 206: pos += 1+len;
! 207: }
! 208: break;
! 209: }
! 210: default:
! 211: return NULL;
! 212: }
! 213:
! 214: return (pos < end) ? pos : NULL;
! 215: }
! 216:
! 217: /**
! 218: * flow4_next_part - an iterator over flowspec components in flowspec binary stream
! 219: * @pos: the beginning of a previous or the first component in flowspec binary
! 220: * stream
! 221: * @end: the last valid byte in scanned flowspec binary stream
! 222: *
! 223: * This function returns a position to the beginning of the next component
! 224: * (to a component type byte) in flowspec binary stream or %NULL for the end.
! 225: */
! 226: inline const byte *
! 227: flow4_next_part(const byte *pos, const byte *end)
! 228: {
! 229: return flow_next_part(pos, end, 0);
! 230: }
! 231:
! 232: /**
! 233: * flow6_next_part - an iterator over flowspec components in flowspec binary stream
! 234: * @pos: the beginning of a previous or the first component in flowspec binary
! 235: * stream
! 236: * @end: the last valid byte in scanned flowspec binary stream
! 237: *
! 238: * This function returns a position to the beginning of the next component
! 239: * (to a component type byte) in flowspec binary stream or %NULL for the end.
! 240: */
! 241: inline const byte *
! 242: flow6_next_part(const byte *pos, const byte *end)
! 243: {
! 244: return flow_next_part(pos, end, 1);
! 245: }
! 246:
! 247:
! 248: /*
! 249: * Flowspec validation
! 250: */
! 251:
! 252: static const char* flow_validated_state_str_[] = {
! 253: [FLOW_ST_UNKNOWN_COMPONENT] = "Unknown component",
! 254: [FLOW_ST_VALID] = "Valid",
! 255: [FLOW_ST_NOT_COMPLETE] = "Not complete",
! 256: [FLOW_ST_EXCEED_MAX_PREFIX_LENGTH] = "Exceed maximal prefix length",
! 257: [FLOW_ST_EXCEED_MAX_PREFIX_OFFSET] = "Exceed maximal prefix offset",
! 258: [FLOW_ST_EXCEED_MAX_VALUE_LENGTH] = "Exceed maximal value length",
! 259: [FLOW_ST_BAD_TYPE_ORDER] = "Bad component order",
! 260: [FLOW_ST_AND_BIT_SHOULD_BE_UNSET] = "The AND-bit should be unset",
! 261: [FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED] = "The Zero-bit should be unset",
! 262: [FLOW_ST_DEST_PREFIX_REQUIRED] = "Destination prefix is missing",
! 263: [FLOW_ST_INVALID_TCP_FLAGS] = "TCP flags exceeding 0xfff",
! 264: [FLOW_ST_CANNOT_USE_DONT_FRAGMENT] = "Cannot use Don't fragment flag in IPv6 flow"
! 265: };
! 266:
! 267: /**
! 268: * flow_validated_state_str - return a textual description of validation process
! 269: * @code: validation result
! 270: *
! 271: * This function return well described validation state in string.
! 272: */
! 273: const char *
! 274: flow_validated_state_str(enum flow_validated_state code)
! 275: {
! 276: return flow_validated_state_str_[code];
! 277: }
! 278:
! 279: static const u8 flow4_max_value_length[] = {
! 280: [FLOW_TYPE_DST_PREFIX] = 0,
! 281: [FLOW_TYPE_SRC_PREFIX] = 0,
! 282: [FLOW_TYPE_IP_PROTOCOL] = 1,
! 283: [FLOW_TYPE_PORT] = 2,
! 284: [FLOW_TYPE_DST_PORT] = 2,
! 285: [FLOW_TYPE_SRC_PORT] = 2,
! 286: [FLOW_TYPE_ICMP_TYPE] = 1,
! 287: [FLOW_TYPE_ICMP_CODE] = 1,
! 288: [FLOW_TYPE_TCP_FLAGS] = 2,
! 289: [FLOW_TYPE_PACKET_LENGTH] = 2,
! 290: [FLOW_TYPE_DSCP] = 1,
! 291: [FLOW_TYPE_FRAGMENT] = 1 /* XXX */
! 292: };
! 293:
! 294: static const u8 flow6_max_value_length[] = {
! 295: [FLOW_TYPE_DST_PREFIX] = 0,
! 296: [FLOW_TYPE_SRC_PREFIX] = 0,
! 297: [FLOW_TYPE_NEXT_HEADER] = 1,
! 298: [FLOW_TYPE_PORT] = 2,
! 299: [FLOW_TYPE_DST_PORT] = 2,
! 300: [FLOW_TYPE_SRC_PORT] = 2,
! 301: [FLOW_TYPE_ICMP_TYPE] = 1,
! 302: [FLOW_TYPE_ICMP_CODE] = 1,
! 303: [FLOW_TYPE_TCP_FLAGS] = 2,
! 304: [FLOW_TYPE_PACKET_LENGTH] = 2,
! 305: [FLOW_TYPE_DSCP] = 1,
! 306: [FLOW_TYPE_FRAGMENT] = 1, /* XXX */
! 307: [FLOW_TYPE_LABEL] = 4
! 308: };
! 309:
! 310: static u8
! 311: flow_max_value_length(enum flow_type type, int ipv6)
! 312: {
! 313: return ipv6 ? flow6_max_value_length[type] : flow4_max_value_length[type];
! 314: }
! 315:
! 316: /**
! 317: * flow_check_cf_bmk_values - check value/bitmask part of flowspec component
! 318: * @fb: flow builder instance
! 319: * @neg: negation operand
! 320: * @val: value from value/mask pair
! 321: * @mask: bitmap mask from value/mask pair
! 322: *
! 323: * This function checks value/bitmask pair. If some problem will appear, the
! 324: * function calls cf_error() function with a textual description of reason
! 325: * to failing of validation.
! 326: */
! 327: void
! 328: flow_check_cf_bmk_values(struct flow_builder *fb, u8 neg, u32 val, u32 mask)
! 329: {
! 330: flow_check_cf_value_length(fb, val);
! 331: flow_check_cf_value_length(fb, mask);
! 332:
! 333: if (neg && !(val == 0 || val == mask))
! 334: cf_error("For negation, value must be zero or bitmask");
! 335:
! 336: if ((fb->this_type == FLOW_TYPE_TCP_FLAGS) && (mask & 0xf000))
! 337: cf_error("Invalid mask 0x%x, must not exceed 0xfff", mask);
! 338:
! 339: if ((fb->this_type == FLOW_TYPE_FRAGMENT) && fb->ipv6 && (mask & 0x01))
! 340: cf_error("Invalid mask 0x%x, bit 0 must be 0", mask);
! 341:
! 342: if (val & ~mask)
! 343: cf_error("Value 0x%x outside bitmask 0x%x", val, mask);
! 344: }
! 345:
! 346: /**
! 347: * flow_check_cf_value_length - check value by flowspec component type
! 348: * @fb: flow builder instance
! 349: * @val: value
! 350: *
! 351: * This function checks if the value is in range of component's type support.
! 352: * If some problem will appear, the function calls cf_error() function with
! 353: * a textual description of reason to failing of validation.
! 354: */
! 355: void
! 356: flow_check_cf_value_length(struct flow_builder *fb, u32 val)
! 357: {
! 358: enum flow_type t = fb->this_type;
! 359: u8 max = flow_max_value_length(t, fb->ipv6);
! 360:
! 361: if (t == FLOW_TYPE_DSCP && val > 0x3f)
! 362: cf_error("%s value %u out of range (0-63)", flow_type_str(t, fb->ipv6), val);
! 363:
! 364: if (max == 1 && (val > 0xff))
! 365: cf_error("%s value %u out of range (0-255)", flow_type_str(t, fb->ipv6), val);
! 366:
! 367: if (max == 2 && (val > 0xffff))
! 368: cf_error("%s value %u out of range (0-65535)", flow_type_str(t, fb->ipv6), val);
! 369: }
! 370:
! 371: static enum flow_validated_state
! 372: flow_validate(const byte *nlri, uint len, int ipv6)
! 373: {
! 374: enum flow_type type = 0;
! 375: const byte *pos = nlri;
! 376: const byte *end = nlri + len;
! 377: int met_dst_pfx = 0;
! 378:
! 379: while (pos < end)
! 380: {
! 381: /* Check increasing type ordering */
! 382: if (*pos <= type)
! 383: return FLOW_ST_BAD_TYPE_ORDER;
! 384: type = *pos++;
! 385:
! 386: switch (type)
! 387: {
! 388: case FLOW_TYPE_DST_PREFIX:
! 389: met_dst_pfx = 1;
! 390: /* Fall through */
! 391: case FLOW_TYPE_SRC_PREFIX:
! 392: {
! 393: uint pxlen = *pos++;
! 394: if (pxlen > (ipv6 ? IP6_MAX_PREFIX_LENGTH : IP4_MAX_PREFIX_LENGTH))
! 395: return FLOW_ST_EXCEED_MAX_PREFIX_LENGTH;
! 396:
! 397: uint bytes = BYTES(pxlen);
! 398: if (ipv6)
! 399: {
! 400: uint pxoffset = *pos++;
! 401: if (pxoffset > IP6_MAX_PREFIX_LENGTH || pxoffset > pxlen)
! 402: return FLOW_ST_EXCEED_MAX_PREFIX_OFFSET;
! 403: bytes -= pxoffset / 8;
! 404: }
! 405: pos += bytes;
! 406:
! 407: break;
! 408: }
! 409:
! 410: case FLOW_TYPE_LABEL:
! 411: if (!ipv6)
! 412: return FLOW_ST_UNKNOWN_COMPONENT;
! 413: /* fall through */
! 414: case FLOW_TYPE_IP_PROTOCOL: /* == FLOW_TYPE_NEXT_HEADER */
! 415: case FLOW_TYPE_PORT:
! 416: case FLOW_TYPE_DST_PORT:
! 417: case FLOW_TYPE_SRC_PORT:
! 418: case FLOW_TYPE_ICMP_TYPE:
! 419: case FLOW_TYPE_ICMP_CODE:
! 420: case FLOW_TYPE_TCP_FLAGS:
! 421: case FLOW_TYPE_PACKET_LENGTH:
! 422: case FLOW_TYPE_DSCP:
! 423: case FLOW_TYPE_FRAGMENT:
! 424: {
! 425: uint last = 0;
! 426: uint first = 1;
! 427:
! 428: while (!last)
! 429: {
! 430: /*
! 431: * 0 1 2 3 4 5 6 7
! 432: * +---+---+---+---+---+---+---+---+
! 433: * | e | a | len | 0 |lt |gt |eq |
! 434: * +---+---+---+---+---+---+---+---+
! 435: *
! 436: * Numeric operator
! 437: */
! 438:
! 439: last = isset_end(pos);
! 440:
! 441: /* The AND bit should in the first operator byte of a sequence */
! 442: if (first && isset_and(pos))
! 443: return FLOW_ST_AND_BIT_SHOULD_BE_UNSET;
! 444:
! 445: /* This bit should be zero */
! 446: if (*pos & 0x08)
! 447: return FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED;
! 448:
! 449: if (type == FLOW_TYPE_TCP_FLAGS || type == FLOW_TYPE_FRAGMENT)
! 450: {
! 451: /*
! 452: * 0 1 2 3 4 5 6 7
! 453: * +---+---+---+---+---+---+---+---+
! 454: * | e | a | len | 0 | 0 |not| m |
! 455: * +---+---+---+---+---+---+---+---+
! 456: *
! 457: * Bitmask operand
! 458: */
! 459: if (*pos & 0x04)
! 460: return FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED;
! 461: }
! 462:
! 463: /* Value length of operator */
! 464: uint len = get_value_length(pos);
! 465: if (len > flow_max_value_length(type, ipv6))
! 466: return FLOW_ST_EXCEED_MAX_VALUE_LENGTH;
! 467:
! 468: /* TCP Flags component must not check highest nibble (just 12 valid bits) */
! 469: if ((type == FLOW_TYPE_TCP_FLAGS) && (len == 2) && (pos[1] & 0xf0))
! 470: return FLOW_ST_INVALID_TCP_FLAGS;
! 471:
! 472: /* Bit-7 must be 0 [draft-ietf-idr-flow-spec-v6] */
! 473: if ((type == FLOW_TYPE_FRAGMENT) && ipv6 && (pos[1] & 0x01))
! 474: return FLOW_ST_CANNOT_USE_DONT_FRAGMENT;
! 475: /* XXX: Could be a fragment component encoded in 2-bytes? */
! 476:
! 477: pos += 1+len;
! 478:
! 479: if (pos > end && !last)
! 480: return FLOW_ST_NOT_COMPLETE;
! 481:
! 482: if (pos > (end+1))
! 483: return FLOW_ST_NOT_COMPLETE;
! 484:
! 485: first = 0;
! 486: }
! 487: break;
! 488: }
! 489: default:
! 490: return FLOW_ST_UNKNOWN_COMPONENT;
! 491: }
! 492: }
! 493:
! 494: if (pos != end)
! 495: return FLOW_ST_NOT_COMPLETE;
! 496:
! 497: if (!ipv6 && !met_dst_pfx)
! 498: return FLOW_ST_DEST_PREFIX_REQUIRED;
! 499:
! 500: return FLOW_ST_VALID;
! 501: }
! 502:
! 503: /**
! 504: * flow4_validate - check untrustworthy IPv4 flowspec data stream
! 505: * @nlri: flowspec data stream without compressed encoded length value
! 506: * @len: length of @nlri
! 507: *
! 508: * This function checks meaningfulness of binary flowspec. It should return
! 509: * %FLOW_ST_VALID or %FLOW_ST_UNKNOWN_COMPONENT. If some problem appears, it
! 510: * returns some other %FLOW_ST_xxx state.
! 511: */
! 512: inline enum flow_validated_state
! 513: flow4_validate(const byte *nlri, uint len)
! 514: {
! 515: return flow_validate(nlri, len, 0);
! 516: }
! 517:
! 518: /**
! 519: * flow6_validate - check untrustworthy IPv6 flowspec data stream
! 520: * @nlri: flowspec binary stream without encoded length value
! 521: * @len: length of @nlri
! 522: *
! 523: * This function checks meaningfulness of binary flowspec. It should return
! 524: * %FLOW_ST_VALID or %FLOW_ST_UNKNOWN_COMPONENT. If some problem appears, it
! 525: * returns some other %FLOW_ST_xxx state.
! 526: */
! 527: inline enum flow_validated_state
! 528: flow6_validate(const byte *nlri, uint len)
! 529: {
! 530: return flow_validate(nlri, len, 1);
! 531: }
! 532:
! 533: /**
! 534: * flow4_validate_cf - validate flowspec data structure &net_addr_flow4 in parsing time
! 535: * @f: flowspec data structure &net_addr_flow4
! 536: *
! 537: * Check if @f is valid flowspec data structure. Can call cf_error() function
! 538: * with a textual description of reason to failing of validation.
! 539: */
! 540: void
! 541: flow4_validate_cf(net_addr_flow4 *f)
! 542: {
! 543: enum flow_validated_state r = flow4_validate(flow4_first_part(f), flow_read_length(f->data));
! 544:
! 545: if (r != FLOW_ST_VALID)
! 546: cf_error("Invalid flow route: %s", flow_validated_state_str(r));
! 547: }
! 548:
! 549: /**
! 550: * flow6_validate_cf - validate flowspec data structure &net_addr_flow6 in parsing time
! 551: * @f: flowspec data structure &net_addr_flow6
! 552: *
! 553: * Check if @f is valid flowspec data structure. Can call cf_error() function
! 554: * with a textual description of reason to failing of validation.
! 555: */
! 556: void
! 557: flow6_validate_cf(net_addr_flow6 *f)
! 558: {
! 559: enum flow_validated_state r = flow6_validate(flow6_first_part(f), flow_read_length(f->data));
! 560:
! 561: if (r != FLOW_ST_VALID)
! 562: cf_error("Invalid flow route: %s", flow_validated_state_str(r));
! 563: }
! 564:
! 565:
! 566: /*
! 567: * Flowspec Builder
! 568: */
! 569:
! 570: /**
! 571: * flow_builder_init - constructor for flowspec builder instance
! 572: * @pool: memory pool
! 573: *
! 574: * This function prepares flowspec builder instance using memory pool @pool.
! 575: */
! 576: struct flow_builder *
! 577: flow_builder_init(pool *pool)
! 578: {
! 579: struct flow_builder *fb = mb_allocz(pool, sizeof(struct flow_builder));
! 580: BUFFER_INIT(fb->data, pool, 4);
! 581: return fb;
! 582: }
! 583:
! 584: static int
! 585: is_stackable_type(enum flow_type type)
! 586: {
! 587: switch (type)
! 588: {
! 589: case FLOW_TYPE_IP_PROTOCOL:
! 590: case FLOW_TYPE_PORT:
! 591: case FLOW_TYPE_DST_PORT:
! 592: case FLOW_TYPE_SRC_PORT:
! 593: case FLOW_TYPE_ICMP_TYPE:
! 594: case FLOW_TYPE_ICMP_CODE:
! 595: case FLOW_TYPE_TCP_FLAGS:
! 596: case FLOW_TYPE_PACKET_LENGTH:
! 597: case FLOW_TYPE_DSCP:
! 598: case FLOW_TYPE_FRAGMENT:
! 599: case FLOW_TYPE_LABEL:
! 600: return 1;
! 601:
! 602: default:
! 603: /* The unknown components are not stack-able in default */
! 604: return 0;
! 605: }
! 606: }
! 607:
! 608: static int
! 609: builder_add_prepare(struct flow_builder *fb)
! 610: {
! 611: if (fb->parts[fb->this_type].length)
! 612: {
! 613: if (fb->last_type != fb->this_type)
! 614: return 0;
! 615:
! 616: if (!is_stackable_type(fb->this_type))
! 617: return 0;
! 618: }
! 619: else
! 620: {
! 621: fb->parts[fb->this_type].offset = fb->data.used;
! 622: }
! 623:
! 624: return 1;
! 625: }
! 626:
! 627: static void
! 628: builder_add_finish(struct flow_builder *fb)
! 629: {
! 630: fb->parts[fb->this_type].length = fb->data.used - fb->parts[fb->this_type].offset;
! 631: flow_builder_set_type(fb, fb->this_type);
! 632: }
! 633:
! 634: static void
! 635: push_pfx_to_buffer(struct flow_builder *fb, u8 pxlen_bytes, byte *ip)
! 636: {
! 637: for (int i = 0; i < pxlen_bytes; i++)
! 638: BUFFER_PUSH(fb->data) = *ip++;
! 639: }
! 640:
! 641: /**
! 642: * flow_builder4_add_pfx - add IPv4 prefix
! 643: * @fb: flowspec builder instance
! 644: * @n4: net address of type IPv4
! 645: *
! 646: * This function add IPv4 prefix into flowspec builder instance.
! 647: */
! 648: int
! 649: flow_builder4_add_pfx(struct flow_builder *fb, const net_addr_ip4 *n4)
! 650: {
! 651: if (!builder_add_prepare(fb))
! 652: return 0;
! 653:
! 654: ip4_addr ip4 = ip4_hton(n4->prefix);
! 655:
! 656: BUFFER_PUSH(fb->data) = fb->this_type;
! 657: BUFFER_PUSH(fb->data) = n4->pxlen;
! 658: push_pfx_to_buffer(fb, BYTES(n4->pxlen), (byte *) &ip4);
! 659:
! 660: builder_add_finish(fb);
! 661: return 1;
! 662: }
! 663:
! 664: /**
! 665: * flow_builder6_add_pfx - add IPv6 prefix
! 666: * @fb: flowspec builder instance
! 667: * @n6: net address of type IPv4
! 668: * @pxoffset: prefix offset for @n6
! 669: *
! 670: * This function add IPv4 prefix into flowspec builder instance. This function
! 671: * should return 1 for successful adding, otherwise returns %0.
! 672: */
! 673: int
! 674: flow_builder6_add_pfx(struct flow_builder *fb, const net_addr_ip6 *n6, u32 pxoffset)
! 675: {
! 676: if (!builder_add_prepare(fb))
! 677: return 0;
! 678:
! 679: ip6_addr ip6 = ip6_hton(n6->prefix);
! 680:
! 681: BUFFER_PUSH(fb->data) = fb->this_type;
! 682: BUFFER_PUSH(fb->data) = n6->pxlen;
! 683: BUFFER_PUSH(fb->data) = pxoffset;
! 684: push_pfx_to_buffer(fb, BYTES(n6->pxlen) - (pxoffset / 8), ((byte *) &ip6) + (pxoffset / 8));
! 685:
! 686: builder_add_finish(fb);
! 687: return 1;
! 688: }
! 689:
! 690: /**
! 691: * flow_builder_add_op_val - add operator/value pair
! 692: * @fb: flowspec builder instance
! 693: * @op: operator
! 694: * @value: value
! 695: *
! 696: * This function add operator/value pair as a part of a flowspec component. It
! 697: * is required to set appropriate flowspec component type using function
! 698: * flow_builder_set_type(). This function should return 1 for successful
! 699: * adding, otherwise returns 0.
! 700: */
! 701: int
! 702: flow_builder_add_op_val(struct flow_builder *fb, byte op, u32 value)
! 703: {
! 704: if (!builder_add_prepare(fb))
! 705: return 0;
! 706:
! 707: if (fb->this_type == fb->last_type)
! 708: {
! 709: /* Remove the end-bit from last operand-value pair of the component */
! 710: fb->data.data[fb->last_op_offset] &= 0x7f;
! 711: }
! 712: else
! 713: {
! 714: BUFFER_PUSH(fb->data) = fb->this_type;
! 715: }
! 716:
! 717: fb->last_op_offset = fb->data.used;
! 718:
! 719: /* Set the end-bit for operand-value pair of the component */
! 720: op |= 0x80;
! 721:
! 722: if (value & 0xff00)
! 723: {
! 724: BUFFER_PUSH(fb->data) = op | 0x10;
! 725: put_u16(BUFFER_INC(fb->data, 2), value);
! 726: }
! 727: else
! 728: {
! 729: BUFFER_PUSH(fb->data) = op;
! 730: BUFFER_PUSH(fb->data) = (u8) value;
! 731: }
! 732:
! 733: builder_add_finish(fb);
! 734: return 1;
! 735: }
! 736:
! 737: /**
! 738: * flow_builder_add_val_mask - add value/bitmask pair
! 739: * @fb: flowspec builder instance
! 740: * @op: operator
! 741: * @value: value
! 742: * @mask: bitmask
! 743: *
! 744: * It is required to set appropriate flowspec component type using function
! 745: * flow_builder_set_type(). This function should return 1 for successful adding,
! 746: * otherwise returns 0.
! 747: */
! 748: int
! 749: flow_builder_add_val_mask(struct flow_builder *fb, byte op, u32 value, u32 mask)
! 750: {
! 751: u32 a = value & mask;
! 752: u32 b = ~value & mask;
! 753:
! 754: if (a)
! 755: {
! 756: flow_builder_add_op_val(fb, op ^ 0x01, a);
! 757: op |= FLOW_OP_AND;
! 758: }
! 759:
! 760: if (b)
! 761: flow_builder_add_op_val(fb, op ^ 0x02, b);
! 762:
! 763: return 1;
! 764: }
! 765:
! 766:
! 767: /**
! 768: * flow_builder_set_type - set type of next flowspec component
! 769: * @fb: flowspec builder instance
! 770: * @type: flowspec component type
! 771: *
! 772: * This function sets type of next flowspec component. It is necessary to call
! 773: * this function before each changing of adding flowspec component.
! 774: */
! 775: void
! 776: flow_builder_set_type(struct flow_builder *fb, enum flow_type type)
! 777: {
! 778: fb->last_type = fb->this_type;
! 779: fb->this_type = type;
! 780: }
! 781:
! 782: static ip4_addr
! 783: flow_read_ip4(const byte *px, uint pxlen)
! 784: {
! 785: ip4_addr ip = IP4_NONE;
! 786: memcpy(&ip, px, BYTES(pxlen));
! 787: return ip4_ntoh(ip);
! 788: }
! 789:
! 790: static ip6_addr
! 791: flow_read_ip6(const byte *px, uint pxlen, uint pxoffset)
! 792: {
! 793: uint floor_offset = BYTES(pxoffset - (pxoffset % 8));
! 794: uint ceil_len = BYTES(pxlen);
! 795: ip6_addr ip = IP6_NONE;
! 796:
! 797: memcpy(((byte *) &ip) + floor_offset, px, ceil_len - floor_offset);
! 798:
! 799: return ip6_ntoh(ip);
! 800: }
! 801:
! 802: static void
! 803: builder_write_parts(struct flow_builder *fb, byte *buf)
! 804: {
! 805: for (int i = 1; i < FLOW_TYPE_MAX; i++)
! 806: {
! 807: if (fb->parts[i].length)
! 808: {
! 809: memcpy(buf, fb->data.data + fb->parts[i].offset, fb->parts[i].length);
! 810: buf += fb->parts[i].length;
! 811: }
! 812: }
! 813: }
! 814:
! 815: /**
! 816: * flow_builder4_finalize - assemble final flowspec data structure &net_addr_flow4
! 817: * @fb: flowspec builder instance
! 818: * @lpool: linear memory pool
! 819: *
! 820: * This function returns final flowspec data structure &net_addr_flow4 allocated
! 821: * onto @lpool linear memory pool.
! 822: */
! 823: net_addr_flow4 *
! 824: flow_builder4_finalize(struct flow_builder *fb, linpool *lpool)
! 825: {
! 826: uint data_len = fb->data.used + (fb->data.used < 0xf0 ? 1 : 2);
! 827: net_addr_flow4 *f = lp_alloc(lpool, sizeof(struct net_addr_flow4) + data_len);
! 828:
! 829: ip4_addr prefix = IP4_NONE;
! 830: uint pxlen = 0;
! 831:
! 832: if (fb->parts[FLOW_TYPE_DST_PREFIX].length)
! 833: {
! 834: byte *p = fb->data.data + fb->parts[FLOW_TYPE_DST_PREFIX].offset + 1;
! 835: pxlen = *p++;
! 836: prefix = flow_read_ip4(p, pxlen);
! 837: }
! 838: *f = NET_ADDR_FLOW4(prefix, pxlen, data_len);
! 839:
! 840: builder_write_parts(fb, f->data + flow_write_length(f->data, fb->data.used));
! 841:
! 842: return f;
! 843: }
! 844:
! 845: /**
! 846: * flow_builder6_finalize - assemble final flowspec data structure &net_addr_flow6
! 847: * @fb: flowspec builder instance
! 848: * @lpool: linear memory pool for allocation of
! 849: *
! 850: * This function returns final flowspec data structure &net_addr_flow6 allocated
! 851: * onto @lpool linear memory pool.
! 852: */
! 853: net_addr_flow6 *
! 854: flow_builder6_finalize(struct flow_builder *fb, linpool *lpool)
! 855: {
! 856: uint data_len = fb->data.used + (fb->data.used < 0xf0 ? 1 : 2);
! 857: net_addr_flow6 *n = lp_alloc(lpool, sizeof(net_addr_flow6) + data_len);
! 858:
! 859: ip6_addr prefix = IP6_NONE;
! 860: uint pxlen = 0;
! 861:
! 862: if (fb->parts[FLOW_TYPE_DST_PREFIX].length)
! 863: {
! 864: byte *p = fb->data.data + fb->parts[FLOW_TYPE_DST_PREFIX].offset + 1;
! 865: pxlen = *p++;
! 866: uint pxoffset = *p++;
! 867: prefix = flow_read_ip6(p, pxlen, pxoffset);
! 868: }
! 869: *n = NET_ADDR_FLOW6(prefix, pxlen, data_len);
! 870:
! 871: builder_write_parts(fb, n->data + flow_write_length(n->data, fb->data.used));
! 872:
! 873: return n;
! 874: }
! 875:
! 876: /**
! 877: * flow_builder_clear - flush flowspec builder instance for another flowspec creation
! 878: * @fb: flowspec builder instance
! 879: *
! 880: * This function flushes all data from builder but it maintains pre-allocated
! 881: * buffer space.
! 882: */
! 883: void
! 884: flow_builder_clear(struct flow_builder *fb)
! 885: {
! 886: BUFFER(byte) data;
! 887: BUFFER_FLUSH(fb->data);
! 888:
! 889: BUFFER_SHALLOW_COPY(data, fb->data);
! 890: memset(fb, 0, sizeof(struct flow_builder));
! 891: BUFFER_SHALLOW_COPY(fb->data, data);
! 892: }
! 893:
! 894:
! 895: /*
! 896: * Net Formatting
! 897: */
! 898:
! 899: /* Flowspec operators for [op, value]+ pairs */
! 900:
! 901: static const char *
! 902: num_op_str(const byte *op)
! 903: {
! 904: switch (*op & 0x07)
! 905: {
! 906: case FLOW_OP_TRUE: return "true";
! 907: case FLOW_OP_EQ: return "=";
! 908: case FLOW_OP_GT: return ">";
! 909: case FLOW_OP_GEQ: return ">=";
! 910: case FLOW_OP_LT: return "<";
! 911: case FLOW_OP_LEQ: return "<=";
! 912: case FLOW_OP_NEQ: return "!=";
! 913: case FLOW_OP_FALSE: return "false";
! 914: }
! 915:
! 916: return NULL;
! 917: }
! 918:
! 919: static uint
! 920: get_value(const byte *val, u8 len)
! 921: {
! 922: switch (len)
! 923: {
! 924: case 1: return *val;
! 925: case 2: return get_u16(val);
! 926: case 4: return get_u32(val);
! 927: // No component may have length 8
! 928: // case 8: return get_u64(val);
! 929: }
! 930:
! 931: return 0;
! 932: }
! 933:
! 934: static const char *
! 935: fragment_val_str(u8 val)
! 936: {
! 937: switch (val)
! 938: {
! 939: case 1: return "dont_fragment";
! 940: case 2: return "is_fragment";
! 941: case 4: return "first_fragment";
! 942: case 8: return "last_fragment";
! 943: }
! 944: return "???";
! 945: }
! 946:
! 947: static void
! 948: net_format_flow_ip(buffer *b, const byte *part, int ipv6)
! 949: {
! 950: uint pxlen = *(part+1);
! 951: if (ipv6)
! 952: {
! 953: uint pxoffset = *(part+2);
! 954: if (pxoffset)
! 955: buffer_print(b, "%I6/%u offset %u; ", flow_read_ip6(part+3,pxlen,pxoffset), pxlen, pxoffset);
! 956: else
! 957: buffer_print(b, "%I6/%u; ", flow_read_ip6(part+3,pxlen,0), pxlen);
! 958: }
! 959: else
! 960: {
! 961: buffer_print(b, "%I4/%u; ", flow_read_ip4(part+2,pxlen), pxlen);
! 962: }
! 963: }
! 964:
! 965: static void
! 966: net_format_flow_num(buffer *b, const byte *part)
! 967: {
! 968: const byte *last_op = NULL;
! 969: const byte *op = part+1;
! 970: uint val;
! 971: uint len;
! 972: uint first = 1;
! 973:
! 974: while (1)
! 975: {
! 976: if (!first)
! 977: {
! 978: /* XXX: I don't like this so complicated if-tree */
! 979: if (!isset_and(op) &&
! 980: ((num_op( op) == FLOW_OP_EQ) || (num_op( op) == FLOW_OP_GEQ)) &&
! 981: ((num_op(last_op) == FLOW_OP_EQ) || (num_op(last_op) == FLOW_OP_LEQ)))
! 982: {
! 983: b->pos--; /* Remove last char (it is a space) */
! 984: buffer_puts(b, ",");
! 985: }
! 986: else
! 987: {
! 988: buffer_puts(b, isset_and(op) ? "&& " : "|| ");
! 989: }
! 990: }
! 991: first = 0;
! 992:
! 993: len = get_value_length(op);
! 994: val = get_value(op+1, len);
! 995:
! 996: if (!isset_end(op) && !isset_and(op) && isset_and(op+1+len) &&
! 997: (num_op(op) == FLOW_OP_GEQ) && (num_op(op+1+len) == FLOW_OP_LEQ))
! 998: {
! 999: /* Display interval */
! 1000: buffer_print(b, "%u..", val);
! 1001: op += 1 + len;
! 1002: len = get_value_length(op);
! 1003: val = get_value(op+1, len);
! 1004: buffer_print(b, "%u", val);
! 1005: }
! 1006: else if (num_op(op) == FLOW_OP_EQ)
! 1007: {
! 1008: buffer_print(b, "%u", val);
! 1009: }
! 1010: else
! 1011: {
! 1012: buffer_print(b, "%s %u", num_op_str(op), val);
! 1013: }
! 1014:
! 1015: if (isset_end(op))
! 1016: {
! 1017: buffer_puts(b, "; ");
! 1018: break;
! 1019: }
! 1020: else
! 1021: {
! 1022: buffer_puts(b, " ");
! 1023: }
! 1024:
! 1025: last_op = op;
! 1026: op += 1 + len;
! 1027: }
! 1028: }
! 1029:
! 1030: static void
! 1031: net_format_flow_bitmask(buffer *b, const byte *part)
! 1032: {
! 1033: const byte *op = part+1;
! 1034: uint val;
! 1035: uint len;
! 1036: uint first = 1;
! 1037:
! 1038: while (1)
! 1039: {
! 1040: if (!first)
! 1041: {
! 1042: if (isset_and(op))
! 1043: {
! 1044: b->pos--; /* Remove last char (it is a space) */
! 1045: buffer_puts(b, ",");
! 1046: }
! 1047: else
! 1048: {
! 1049: buffer_puts(b, "|| ");
! 1050: }
! 1051: }
! 1052: first = 0;
! 1053:
! 1054: len = get_value_length(op);
! 1055: val = get_value(op+1, len);
! 1056:
! 1057: /*
! 1058: * Not Match Show
! 1059: * ------------------
! 1060: * 0 0 !0/B
! 1061: * 0 1 B/B
! 1062: * 1 0 0/B
! 1063: * 1 1 !B/B
! 1064: */
! 1065:
! 1066: if ((*op & 0x3) == 0x3 || (*op & 0x3) == 0)
! 1067: buffer_puts(b, "!");
! 1068:
! 1069: if (*part == FLOW_TYPE_FRAGMENT && (val == 1 || val == 2 || val == 4 || val == 8))
! 1070: buffer_print(b, "%s%s", ((*op & 0x1) ? "" : "!"), fragment_val_str(val));
! 1071: else
! 1072: buffer_print(b, "0x%x/0x%x", ((*op & 0x1) ? val : 0), val);
! 1073:
! 1074: if (isset_end(op))
! 1075: {
! 1076: buffer_puts(b, "; ");
! 1077: break;
! 1078: }
! 1079: else
! 1080: {
! 1081: buffer_puts(b, " ");
! 1082: }
! 1083:
! 1084: op += 1 + len;
! 1085: }
! 1086: }
! 1087:
! 1088: static uint
! 1089: net_format_flow(char *buf, uint blen, const byte *data, uint dlen, int ipv6)
! 1090: {
! 1091: buffer b = {
! 1092: .start = buf,
! 1093: .pos = buf,
! 1094: .end = buf + blen,
! 1095: };
! 1096:
! 1097: const byte *part = flow_first_part(data);
! 1098: *buf = 0;
! 1099:
! 1100: if (ipv6)
! 1101: buffer_puts(&b, "flow6 { ");
! 1102: else
! 1103: buffer_puts(&b, "flow4 { ");
! 1104:
! 1105: while (part)
! 1106: {
! 1107: buffer_print(&b, "%s ", flow_type_str(*part, ipv6));
! 1108:
! 1109: switch (*part)
! 1110: {
! 1111: case FLOW_TYPE_DST_PREFIX:
! 1112: case FLOW_TYPE_SRC_PREFIX:
! 1113: net_format_flow_ip(&b, part, ipv6);
! 1114: break;
! 1115: case FLOW_TYPE_IP_PROTOCOL: /* == FLOW_TYPE_NEXT_HEADER */
! 1116: case FLOW_TYPE_PORT:
! 1117: case FLOW_TYPE_DST_PORT:
! 1118: case FLOW_TYPE_SRC_PORT:
! 1119: case FLOW_TYPE_ICMP_TYPE:
! 1120: case FLOW_TYPE_ICMP_CODE:
! 1121: case FLOW_TYPE_PACKET_LENGTH:
! 1122: case FLOW_TYPE_DSCP:
! 1123: net_format_flow_num(&b, part);
! 1124: break;
! 1125: case FLOW_TYPE_TCP_FLAGS:
! 1126: case FLOW_TYPE_FRAGMENT:
! 1127: case FLOW_TYPE_LABEL:
! 1128: net_format_flow_bitmask(&b, part);
! 1129: break;
! 1130: }
! 1131:
! 1132: part = flow_next_part(part, data+dlen, ipv6);
! 1133: }
! 1134:
! 1135: buffer_puts(&b, "}");
! 1136:
! 1137: if (b.pos == b.end)
! 1138: {
! 1139: b.pos = b.start + MIN(blen - 6, strlen(b.start));
! 1140: buffer_puts(&b, " ...}");
! 1141: }
! 1142:
! 1143: return b.pos - b.start;
! 1144: }
! 1145:
! 1146: /**
! 1147: * flow4_net_format - stringify flowspec data structure &net_addr_flow4
! 1148: * @buf: pre-allocated buffer for writing a stringify net address flowspec
! 1149: * @blen: free allocated space in @buf
! 1150: * @f: flowspec data structure &net_addr_flow4 for stringify
! 1151: *
! 1152: * This function writes stringified @f into @buf. The function returns number
! 1153: * of written chars. If final string is too large, the string will ends the with
! 1154: * ' ...}' sequence and zero-terminator.
! 1155: */
! 1156: uint
! 1157: flow4_net_format(char *buf, uint blen, const net_addr_flow4 *f)
! 1158: {
! 1159: return net_format_flow(buf, blen, f->data, f->length - sizeof(net_addr_flow4), 0);
! 1160: }
! 1161:
! 1162: /**
! 1163: * flow6_net_format - stringify flowspec data structure &net_addr_flow6
! 1164: * @buf: pre-allocated buffer for writing a stringify net address flowspec
! 1165: * @blen: free allocated space in @buf
! 1166: * @f: flowspec data structure &net_addr_flow4 for stringify
! 1167: *
! 1168: * This function writes stringified @f into @buf. The function returns number
! 1169: * of written chars. If final string is too large, the string will ends the with
! 1170: * ' ...}' sequence and zero-terminator.
! 1171: */
! 1172: uint
! 1173: flow6_net_format(char *buf, uint blen, const net_addr_flow6 *f)
! 1174: {
! 1175: return net_format_flow(buf, blen, f->data, f->length - sizeof(net_addr_flow6), 1);
! 1176: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>