ipv6.c revision 241883
1187770Sluigi/* 2187770Sluigi * Copyright (c) 2002-2003 Luigi Rizzo 3187770Sluigi * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp 4187770Sluigi * Copyright (c) 1994 Ugen J.S.Antsilevich 5187770Sluigi * 6187770Sluigi * Idea and grammar partially left from: 7187770Sluigi * Copyright (c) 1993 Daniel Boulet 8187770Sluigi * 9187770Sluigi * Redistribution and use in source forms, with and without modification, 10187770Sluigi * are permitted provided that this entire comment appears intact. 11187770Sluigi * 12187770Sluigi * Redistribution in binary form may occur without any restrictions. 13187770Sluigi * Obviously, it would be nice if you gave credit where credit is due 14187770Sluigi * but requiring it would be too onerous. 15187770Sluigi * 16187770Sluigi * This software is provided ``AS IS'' without any warranties of any kind. 17187770Sluigi * 18187770Sluigi * NEW command line interface for IP firewall facility 19187770Sluigi * 20187770Sluigi * $FreeBSD: stable/9/sbin/ipfw/ipv6.c 241883 2012-10-22 12:39:57Z melifaro $ 21187770Sluigi * 22187770Sluigi * ipv6 support 23187770Sluigi */ 24187770Sluigi 25187770Sluigi#include <sys/types.h> 26187770Sluigi#include <sys/socket.h> 27187770Sluigi 28187770Sluigi#include "ipfw2.h" 29187770Sluigi 30187770Sluigi#include <err.h> 31187770Sluigi#include <netdb.h> 32187770Sluigi#include <stdio.h> 33187770Sluigi#include <stdlib.h> 34187770Sluigi#include <string.h> 35187770Sluigi#include <sysexits.h> 36187770Sluigi 37187770Sluigi#include <net/if.h> 38187770Sluigi#include <netinet/in.h> 39187770Sluigi#include <netinet/in_systm.h> 40187770Sluigi#include <netinet/ip.h> 41187770Sluigi#include <netinet/icmp6.h> 42187770Sluigi#include <netinet/ip_fw.h> 43187770Sluigi#include <arpa/inet.h> 44187770Sluigi 45187770Sluigistatic struct _s_x icmp6codes[] = { 46187770Sluigi { "no-route", ICMP6_DST_UNREACH_NOROUTE }, 47187770Sluigi { "admin-prohib", ICMP6_DST_UNREACH_ADMIN }, 48187770Sluigi { "address", ICMP6_DST_UNREACH_ADDR }, 49187770Sluigi { "port", ICMP6_DST_UNREACH_NOPORT }, 50187770Sluigi { NULL, 0 } 51187770Sluigi}; 52187770Sluigi 53187770Sluigivoid 54187770Sluigifill_unreach6_code(u_short *codep, char *str) 55187770Sluigi{ 56187770Sluigi int val; 57187770Sluigi char *s; 58187770Sluigi 59187770Sluigi val = strtoul(str, &s, 0); 60187770Sluigi if (s == str || *s != '\0' || val >= 0x100) 61187770Sluigi val = match_token(icmp6codes, str); 62187770Sluigi if (val < 0) 63187770Sluigi errx(EX_DATAERR, "unknown ICMPv6 unreachable code ``%s''", str); 64187770Sluigi *codep = val; 65187770Sluigi return; 66187770Sluigi} 67187770Sluigi 68187770Sluigivoid 69187770Sluigiprint_unreach6_code(uint16_t code) 70187770Sluigi{ 71187770Sluigi char const *s = match_value(icmp6codes, code); 72187770Sluigi 73187770Sluigi if (s != NULL) 74187770Sluigi printf("unreach6 %s", s); 75187770Sluigi else 76187770Sluigi printf("unreach6 %u", code); 77187770Sluigi} 78187770Sluigi 79220802Sglebius/* 80187770Sluigi * Print the ip address contained in a command. 81187770Sluigi */ 82187770Sluigivoid 83187770Sluigiprint_ip6(ipfw_insn_ip6 *cmd, char const *s) 84187770Sluigi{ 85187770Sluigi struct hostent *he = NULL; 86187770Sluigi int len = F_LEN((ipfw_insn *) cmd) - 1; 87187770Sluigi struct in6_addr *a = &(cmd->addr6); 88187770Sluigi char trad[255]; 89187770Sluigi 90187770Sluigi printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s); 91187770Sluigi 92187770Sluigi if (cmd->o.opcode == O_IP6_SRC_ME || cmd->o.opcode == O_IP6_DST_ME) { 93220802Sglebius printf("me6"); 94220802Sglebius return; 95187770Sluigi } 96187770Sluigi if (cmd->o.opcode == O_IP6) { 97220802Sglebius printf(" ip6"); 98220802Sglebius return; 99187770Sluigi } 100187770Sluigi 101187770Sluigi /* 102220802Sglebius * len == 4 indicates a single IP, whereas lists of 1 or more 103220802Sglebius * addr/mask pairs have len = (2n+1). We convert len to n so we 104220802Sglebius * use that to count the number of entries. 105220802Sglebius */ 106187770Sluigi 107187770Sluigi for (len = len / 4; len > 0; len -= 2, a += 2) { 108220802Sglebius int mb = /* mask length */ 109220802Sglebius (cmd->o.opcode == O_IP6_SRC || cmd->o.opcode == O_IP6_DST) ? 110220802Sglebius 128 : contigmask((uint8_t *)&(a[1]), 128); 111187770Sluigi 112220802Sglebius if (mb == 128 && co.do_resolv) 113220802Sglebius he = gethostbyaddr((char *)a, sizeof(*a), AF_INET6); 114220802Sglebius if (he != NULL) /* resolved to name */ 115220802Sglebius printf("%s", he->h_name); 116220802Sglebius else if (mb == 0) /* any */ 117220802Sglebius printf("any"); 118220802Sglebius else { /* numeric IP followed by some kind of mask */ 119220802Sglebius if (inet_ntop(AF_INET6, a, trad, sizeof( trad ) ) == NULL) 120220802Sglebius printf("Error ntop in print_ip6\n"); 121220802Sglebius printf("%s", trad ); 122220802Sglebius if (mb < 0) /* XXX not really legal... */ 123220802Sglebius printf(":%s", 124220802Sglebius inet_ntop(AF_INET6, &a[1], trad, sizeof(trad))); 125220802Sglebius else if (mb < 128) 126220802Sglebius printf("/%d", mb); 127220802Sglebius } 128220802Sglebius if (len > 2) 129220802Sglebius printf(","); 130187770Sluigi } 131187770Sluigi} 132187770Sluigi 133187770Sluigivoid 134187770Sluigifill_icmp6types(ipfw_insn_icmp6 *cmd, char *av) 135187770Sluigi{ 136187770Sluigi uint8_t type; 137187770Sluigi 138187770Sluigi bzero(cmd, sizeof(*cmd)); 139187770Sluigi while (*av) { 140220802Sglebius if (*av == ',') 141220802Sglebius av++; 142220802Sglebius type = strtoul(av, &av, 0); 143220802Sglebius if (*av != ',' && *av != '\0') 144220802Sglebius errx(EX_DATAERR, "invalid ICMP6 type"); 145187770Sluigi /* 146187770Sluigi * XXX: shouldn't this be 0xFF? I can't see any reason why 147187770Sluigi * we shouldn't be able to filter all possiable values 148187770Sluigi * regardless of the ability of the rest of the kernel to do 149187770Sluigi * anything useful with them. 150187770Sluigi */ 151220802Sglebius if (type > ICMP6_MAXTYPE) 152220802Sglebius errx(EX_DATAERR, "ICMP6 type out of range"); 153220802Sglebius cmd->d[type / 32] |= ( 1 << (type % 32)); 154187770Sluigi } 155187770Sluigi cmd->o.opcode = O_ICMP6TYPE; 156187770Sluigi cmd->o.len |= F_INSN_SIZE(ipfw_insn_icmp6); 157187770Sluigi} 158187770Sluigi 159187770Sluigi 160187770Sluigivoid 161187770Sluigiprint_icmp6types(ipfw_insn_u32 *cmd) 162187770Sluigi{ 163187770Sluigi int i, j; 164187770Sluigi char sep= ' '; 165187770Sluigi 166187770Sluigi printf(" ip6 icmp6types"); 167187770Sluigi for (i = 0; i < 7; i++) 168220802Sglebius for (j=0; j < 32; ++j) { 169220802Sglebius if ( (cmd->d[i] & (1 << (j))) == 0) 170220802Sglebius continue; 171220802Sglebius printf("%c%d", sep, (i*32 + j)); 172220802Sglebius sep = ','; 173220802Sglebius } 174187770Sluigi} 175187770Sluigi 176187770Sluigivoid 177187770Sluigiprint_flow6id( ipfw_insn_u32 *cmd) 178187770Sluigi{ 179187770Sluigi uint16_t i, limit = cmd->o.arg1; 180187770Sluigi char sep = ','; 181187770Sluigi 182187770Sluigi printf(" flow-id "); 183187770Sluigi for( i=0; i < limit; ++i) { 184220802Sglebius if (i == limit - 1) 185220802Sglebius sep = ' '; 186220802Sglebius printf("%d%c", cmd->d[i], sep); 187187770Sluigi } 188187770Sluigi} 189187770Sluigi 190187770Sluigi/* structure and define for the extension header in ipv6 */ 191187770Sluigistatic struct _s_x ext6hdrcodes[] = { 192187770Sluigi { "frag", EXT_FRAGMENT }, 193187770Sluigi { "hopopt", EXT_HOPOPTS }, 194187770Sluigi { "route", EXT_ROUTING }, 195187770Sluigi { "dstopt", EXT_DSTOPTS }, 196220802Sglebius { "ah", EXT_AH }, 197220802Sglebius { "esp", EXT_ESP }, 198187770Sluigi { "rthdr0", EXT_RTHDR0 }, 199187770Sluigi { "rthdr2", EXT_RTHDR2 }, 200220802Sglebius { NULL, 0 } 201187770Sluigi}; 202187770Sluigi 203187770Sluigi/* fills command for the extension header filtering */ 204187770Sluigiint 205187770Sluigifill_ext6hdr( ipfw_insn *cmd, char *av) 206187770Sluigi{ 207187770Sluigi int tok; 208187770Sluigi char *s = av; 209187770Sluigi 210187770Sluigi cmd->arg1 = 0; 211187770Sluigi 212187770Sluigi while(s) { 213220802Sglebius av = strsep( &s, ",") ; 214220802Sglebius tok = match_token(ext6hdrcodes, av); 215220802Sglebius switch (tok) { 216220802Sglebius case EXT_FRAGMENT: 217220802Sglebius cmd->arg1 |= EXT_FRAGMENT; 218220802Sglebius break; 219187770Sluigi 220220802Sglebius case EXT_HOPOPTS: 221220802Sglebius cmd->arg1 |= EXT_HOPOPTS; 222220802Sglebius break; 223187770Sluigi 224220802Sglebius case EXT_ROUTING: 225220802Sglebius cmd->arg1 |= EXT_ROUTING; 226220802Sglebius break; 227187770Sluigi 228220802Sglebius case EXT_DSTOPTS: 229220802Sglebius cmd->arg1 |= EXT_DSTOPTS; 230220802Sglebius break; 231187770Sluigi 232220802Sglebius case EXT_AH: 233220802Sglebius cmd->arg1 |= EXT_AH; 234220802Sglebius break; 235187770Sluigi 236220802Sglebius case EXT_ESP: 237220802Sglebius cmd->arg1 |= EXT_ESP; 238220802Sglebius break; 239187770Sluigi 240220802Sglebius case EXT_RTHDR0: 241220802Sglebius cmd->arg1 |= EXT_RTHDR0; 242220802Sglebius break; 243187770Sluigi 244220802Sglebius case EXT_RTHDR2: 245220802Sglebius cmd->arg1 |= EXT_RTHDR2; 246220802Sglebius break; 247187770Sluigi 248220802Sglebius default: 249220802Sglebius errx( EX_DATAERR, "invalid option for ipv6 exten header" ); 250220802Sglebius break; 251220802Sglebius } 252187770Sluigi } 253187770Sluigi if (cmd->arg1 == 0 ) 254220802Sglebius return 0; 255187770Sluigi cmd->opcode = O_EXT_HDR; 256187770Sluigi cmd->len |= F_INSN_SIZE( ipfw_insn ); 257187770Sluigi return 1; 258187770Sluigi} 259187770Sluigi 260187770Sluigivoid 261187770Sluigiprint_ext6hdr( ipfw_insn *cmd ) 262187770Sluigi{ 263187770Sluigi char sep = ' '; 264187770Sluigi 265187770Sluigi printf(" extension header:"); 266187770Sluigi if (cmd->arg1 & EXT_FRAGMENT ) { 267220802Sglebius printf("%cfragmentation", sep); 268220802Sglebius sep = ','; 269187770Sluigi } 270187770Sluigi if (cmd->arg1 & EXT_HOPOPTS ) { 271220802Sglebius printf("%chop options", sep); 272220802Sglebius sep = ','; 273187770Sluigi } 274187770Sluigi if (cmd->arg1 & EXT_ROUTING ) { 275220802Sglebius printf("%crouting options", sep); 276220802Sglebius sep = ','; 277187770Sluigi } 278187770Sluigi if (cmd->arg1 & EXT_RTHDR0 ) { 279220802Sglebius printf("%crthdr0", sep); 280220802Sglebius sep = ','; 281187770Sluigi } 282187770Sluigi if (cmd->arg1 & EXT_RTHDR2 ) { 283220802Sglebius printf("%crthdr2", sep); 284220802Sglebius sep = ','; 285187770Sluigi } 286187770Sluigi if (cmd->arg1 & EXT_DSTOPTS ) { 287220802Sglebius printf("%cdestination options", sep); 288220802Sglebius sep = ','; 289187770Sluigi } 290187770Sluigi if (cmd->arg1 & EXT_AH ) { 291220802Sglebius printf("%cauthentication header", sep); 292220802Sglebius sep = ','; 293187770Sluigi } 294187770Sluigi if (cmd->arg1 & EXT_ESP ) { 295220802Sglebius printf("%cencapsulated security payload", sep); 296187770Sluigi } 297187770Sluigi} 298187770Sluigi 299187770Sluigi/* Try to find ipv6 address by hostname */ 300187770Sluigistatic int 301187770Sluigilookup_host6 (char *host, struct in6_addr *ip6addr) 302187770Sluigi{ 303187770Sluigi struct hostent *he; 304187770Sluigi 305187770Sluigi if (!inet_pton(AF_INET6, host, ip6addr)) { 306187770Sluigi if ((he = gethostbyname2(host, AF_INET6)) == NULL) 307187770Sluigi return(-1); 308187770Sluigi memcpy(ip6addr, he->h_addr_list[0], sizeof( struct in6_addr)); 309187770Sluigi } 310187770Sluigi return(0); 311187770Sluigi} 312187770Sluigi 313187770Sluigi 314187770Sluigi/* 315187770Sluigi * fill the addr and mask fields in the instruction as appropriate from av. 316187770Sluigi * Update length as appropriate. 317187770Sluigi * The following formats are allowed: 318187770Sluigi * any matches any IP6. Actually returns an empty instruction. 319187770Sluigi * me returns O_IP6_*_ME 320187770Sluigi * 321220802Sglebius * 03f1::234:123:0342 single IP6 addres 322220802Sglebius * 03f1::234:123:0342/24 address/mask 323220802Sglebius * 03f1::234:123:0342/24,03f1::234:123:0343/ List of address 324187770Sluigi * 325187770Sluigi * Set of address (as in ipv6) not supported because ipv6 address 326187770Sluigi * are typically random past the initial prefix. 327187770Sluigi * Return 1 on success, 0 on failure. 328187770Sluigi */ 329187770Sluigistatic int 330187770Sluigifill_ip6(ipfw_insn_ip6 *cmd, char *av) 331187770Sluigi{ 332187770Sluigi int len = 0; 333187770Sluigi struct in6_addr *d = &(cmd->addr6); 334187770Sluigi /* 335187770Sluigi * Needed for multiple address. 336187770Sluigi * Note d[1] points to struct in6_add r mask6 of cmd 337187770Sluigi */ 338187770Sluigi 339241883Smelifaro cmd->o.len &= ~F_LEN_MASK; /* zero len */ 340187770Sluigi 341241883Smelifaro if (strcmp(av, "any") == 0) 342241883Smelifaro return (1); 343187770Sluigi 344187770Sluigi 345241883Smelifaro if (strcmp(av, "me") == 0) { /* Set the data for "me" opt*/ 346241883Smelifaro cmd->o.len |= F_INSN_SIZE(ipfw_insn); 347241883Smelifaro return (1); 348241883Smelifaro } 349187770Sluigi 350241883Smelifaro if (strcmp(av, "me6") == 0) { /* Set the data for "me" opt*/ 351241883Smelifaro cmd->o.len |= F_INSN_SIZE(ipfw_insn); 352241883Smelifaro return (1); 353241883Smelifaro } 354187770Sluigi 355241883Smelifaro if (strncmp(av, "table(", 6) == 0) { 356241883Smelifaro char *p = strchr(av + 6, ','); 357241883Smelifaro uint32_t *dm = ((ipfw_insn_u32 *)cmd)->d; 358241883Smelifaro 359241883Smelifaro if (p) 360241883Smelifaro *p++ = '\0'; 361241883Smelifaro cmd->o.opcode = O_IP_DST_LOOKUP; 362241883Smelifaro cmd->o.arg1 = strtoul(av + 6, NULL, 0); 363241883Smelifaro if (p) { 364241883Smelifaro cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32); 365241883Smelifaro dm[0] = strtoul(p, NULL, 0); 366241883Smelifaro } else 367241883Smelifaro cmd->o.len |= F_INSN_SIZE(ipfw_insn); 368241883Smelifaro return (1); 369241883Smelifaro } 370241883Smelifaro 371241883Smelifaro av = strdup(av); 372241883Smelifaro while (av) { 373187770Sluigi /* 374187770Sluigi * After the address we can have '/' indicating a mask, 375187770Sluigi * or ',' indicating another address follows. 376187770Sluigi */ 377187770Sluigi 378187770Sluigi char *p; 379187770Sluigi int masklen; 380187770Sluigi char md = '\0'; 381187770Sluigi 382187770Sluigi if ((p = strpbrk(av, "/,")) ) { 383187770Sluigi md = *p; /* save the separator */ 384187770Sluigi *p = '\0'; /* terminate address string */ 385187770Sluigi p++; /* and skip past it */ 386187770Sluigi } 387187770Sluigi /* now p points to NULL, mask or next entry */ 388187770Sluigi 389187770Sluigi /* lookup stores address in *d as a side effect */ 390187770Sluigi if (lookup_host6(av, d) != 0) { 391187770Sluigi /* XXX: failed. Free memory and go */ 392187770Sluigi errx(EX_DATAERR, "bad address \"%s\"", av); 393187770Sluigi } 394187770Sluigi /* next, look at the mask, if any */ 395187770Sluigi masklen = (md == '/') ? atoi(p) : 128; 396187770Sluigi if (masklen > 128 || masklen < 0) 397187770Sluigi errx(EX_DATAERR, "bad width \"%s\''", p); 398187770Sluigi else 399187770Sluigi n2mask(&d[1], masklen); 400187770Sluigi 401187770Sluigi APPLY_MASK(d, &d[1]) /* mask base address with mask */ 402187770Sluigi 403187770Sluigi /* find next separator */ 404187770Sluigi 405187770Sluigi if (md == '/') { /* find separator past the mask */ 406187770Sluigi p = strpbrk(p, ","); 407187770Sluigi if (p != NULL) 408187770Sluigi p++; 409187770Sluigi } 410187770Sluigi av = p; 411187770Sluigi 412187770Sluigi /* Check this entry */ 413187770Sluigi if (masklen == 0) { 414187770Sluigi /* 415187770Sluigi * 'any' turns the entire list into a NOP. 416187770Sluigi * 'not any' never matches, so it is removed from the 417187770Sluigi * list unless it is the only item, in which case we 418187770Sluigi * report an error. 419187770Sluigi */ 420187770Sluigi if (cmd->o.len & F_NOT && av == NULL && len == 0) 421187770Sluigi errx(EX_DATAERR, "not any never matches"); 422187770Sluigi continue; 423187770Sluigi } 424187770Sluigi 425187770Sluigi /* 426187770Sluigi * A single IP can be stored alone 427187770Sluigi */ 428187770Sluigi if (masklen == 128 && av == NULL && len == 0) { 429187770Sluigi len = F_INSN_SIZE(struct in6_addr); 430187770Sluigi break; 431187770Sluigi } 432187770Sluigi 433187770Sluigi /* Update length and pointer to arguments */ 434187770Sluigi len += F_INSN_SIZE(struct in6_addr)*2; 435187770Sluigi d += 2; 436187770Sluigi } /* end while */ 437187770Sluigi 438187770Sluigi /* 439187770Sluigi * Total length of the command, remember that 1 is the size of 440187770Sluigi * the base command. 441187770Sluigi */ 442187770Sluigi if (len + 1 > F_LEN_MASK) 443187770Sluigi errx(EX_DATAERR, "address list too long"); 444187770Sluigi cmd->o.len |= len+1; 445187770Sluigi free(av); 446187770Sluigi return (1); 447187770Sluigi} 448187770Sluigi 449187770Sluigi/* 450187770Sluigi * fills command for ipv6 flow-id filtering 451187770Sluigi * note that the 20 bit flow number is stored in a array of u_int32_t 452187770Sluigi * it's supported lists of flow-id, so in the o.arg1 we store how many 453187770Sluigi * additional flow-id we want to filter, the basic is 1 454187770Sluigi */ 455187770Sluigivoid 456187770Sluigifill_flow6( ipfw_insn_u32 *cmd, char *av ) 457187770Sluigi{ 458187770Sluigi u_int32_t type; /* Current flow number */ 459187770Sluigi u_int16_t nflow = 0; /* Current flow index */ 460187770Sluigi char *s = av; 461187770Sluigi cmd->d[0] = 0; /* Initializing the base number*/ 462187770Sluigi 463187770Sluigi while (s) { 464187770Sluigi av = strsep( &s, ",") ; 465187770Sluigi type = strtoul(av, &av, 0); 466187770Sluigi if (*av != ',' && *av != '\0') 467187770Sluigi errx(EX_DATAERR, "invalid ipv6 flow number %s", av); 468187770Sluigi if (type > 0xfffff) 469187770Sluigi errx(EX_DATAERR, "flow number out of range %s", av); 470187770Sluigi cmd->d[nflow] |= type; 471187770Sluigi nflow++; 472187770Sluigi } 473187770Sluigi if( nflow > 0 ) { 474187770Sluigi cmd->o.opcode = O_FLOW6ID; 475187770Sluigi cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + nflow; 476187770Sluigi cmd->o.arg1 = nflow; 477187770Sluigi } 478187770Sluigi else { 479187770Sluigi errx(EX_DATAERR, "invalid ipv6 flow number %s", av); 480187770Sluigi } 481187770Sluigi} 482187770Sluigi 483187770Sluigiipfw_insn * 484187770Sluigiadd_srcip6(ipfw_insn *cmd, char *av) 485187770Sluigi{ 486187770Sluigi 487187770Sluigi fill_ip6((ipfw_insn_ip6 *)cmd, av); 488241883Smelifaro if (cmd->opcode == O_IP_DST_SET) /* set */ 489241883Smelifaro cmd->opcode = O_IP_SRC_SET; 490241883Smelifaro else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */ 491241883Smelifaro cmd->opcode = O_IP_SRC_LOOKUP; 492241883Smelifaro else if (F_LEN(cmd) == 0) { /* any */ 493187770Sluigi } else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) { /* "me" */ 494187770Sluigi cmd->opcode = O_IP6_SRC_ME; 495187770Sluigi } else if (F_LEN(cmd) == 496187770Sluigi (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) { 497187770Sluigi /* single IP, no mask*/ 498187770Sluigi cmd->opcode = O_IP6_SRC; 499187770Sluigi } else { /* addr/mask opt */ 500187770Sluigi cmd->opcode = O_IP6_SRC_MASK; 501187770Sluigi } 502187770Sluigi return cmd; 503187770Sluigi} 504187770Sluigi 505187770Sluigiipfw_insn * 506187770Sluigiadd_dstip6(ipfw_insn *cmd, char *av) 507187770Sluigi{ 508187770Sluigi 509187770Sluigi fill_ip6((ipfw_insn_ip6 *)cmd, av); 510241883Smelifaro if (cmd->opcode == O_IP_DST_SET) /* set */ 511241883Smelifaro ; 512241883Smelifaro else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */ 513241883Smelifaro ; 514241883Smelifaro else if (F_LEN(cmd) == 0) { /* any */ 515187770Sluigi } else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) { /* "me" */ 516187770Sluigi cmd->opcode = O_IP6_DST_ME; 517187770Sluigi } else if (F_LEN(cmd) == 518187770Sluigi (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) { 519187770Sluigi /* single IP, no mask*/ 520187770Sluigi cmd->opcode = O_IP6_DST; 521187770Sluigi } else { /* addr/mask opt */ 522187770Sluigi cmd->opcode = O_IP6_DST_MASK; 523187770Sluigi } 524187770Sluigi return cmd; 525187770Sluigi} 526