Annotation of embedaddon/bird2/lib/flowspec.c, revision 1.1.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>