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$ 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 45248505Smelifaro#define CHECK_LENGTH(v, len) do { \ 46248505Smelifaro if ((v) < (len)) \ 47248505Smelifaro errx(EX_DATAERR, "Rule too long"); \ 48248505Smelifaro } while (0) 49248505Smelifaro 50187770Sluigistatic struct _s_x icmp6codes[] = { 51187770Sluigi { "no-route", ICMP6_DST_UNREACH_NOROUTE }, 52187770Sluigi { "admin-prohib", ICMP6_DST_UNREACH_ADMIN }, 53187770Sluigi { "address", ICMP6_DST_UNREACH_ADDR }, 54187770Sluigi { "port", ICMP6_DST_UNREACH_NOPORT }, 55187770Sluigi { NULL, 0 } 56187770Sluigi}; 57187770Sluigi 58187770Sluigivoid 59187770Sluigifill_unreach6_code(u_short *codep, char *str) 60187770Sluigi{ 61187770Sluigi int val; 62187770Sluigi char *s; 63187770Sluigi 64187770Sluigi val = strtoul(str, &s, 0); 65187770Sluigi if (s == str || *s != '\0' || val >= 0x100) 66187770Sluigi val = match_token(icmp6codes, str); 67187770Sluigi if (val < 0) 68187770Sluigi errx(EX_DATAERR, "unknown ICMPv6 unreachable code ``%s''", str); 69187770Sluigi *codep = val; 70187770Sluigi return; 71187770Sluigi} 72187770Sluigi 73187770Sluigivoid 74187770Sluigiprint_unreach6_code(uint16_t code) 75187770Sluigi{ 76187770Sluigi char const *s = match_value(icmp6codes, code); 77187770Sluigi 78187770Sluigi if (s != NULL) 79187770Sluigi printf("unreach6 %s", s); 80187770Sluigi else 81187770Sluigi printf("unreach6 %u", code); 82187770Sluigi} 83187770Sluigi 84220802Sglebius/* 85187770Sluigi * Print the ip address contained in a command. 86187770Sluigi */ 87187770Sluigivoid 88187770Sluigiprint_ip6(ipfw_insn_ip6 *cmd, char const *s) 89187770Sluigi{ 90187770Sluigi struct hostent *he = NULL; 91187770Sluigi int len = F_LEN((ipfw_insn *) cmd) - 1; 92187770Sluigi struct in6_addr *a = &(cmd->addr6); 93187770Sluigi char trad[255]; 94187770Sluigi 95187770Sluigi printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s); 96187770Sluigi 97187770Sluigi if (cmd->o.opcode == O_IP6_SRC_ME || cmd->o.opcode == O_IP6_DST_ME) { 98220802Sglebius printf("me6"); 99220802Sglebius return; 100187770Sluigi } 101187770Sluigi if (cmd->o.opcode == O_IP6) { 102220802Sglebius printf(" ip6"); 103220802Sglebius return; 104187770Sluigi } 105187770Sluigi 106187770Sluigi /* 107220802Sglebius * len == 4 indicates a single IP, whereas lists of 1 or more 108220802Sglebius * addr/mask pairs have len = (2n+1). We convert len to n so we 109220802Sglebius * use that to count the number of entries. 110220802Sglebius */ 111187770Sluigi 112187770Sluigi for (len = len / 4; len > 0; len -= 2, a += 2) { 113220802Sglebius int mb = /* mask length */ 114220802Sglebius (cmd->o.opcode == O_IP6_SRC || cmd->o.opcode == O_IP6_DST) ? 115220802Sglebius 128 : contigmask((uint8_t *)&(a[1]), 128); 116187770Sluigi 117220802Sglebius if (mb == 128 && co.do_resolv) 118220802Sglebius he = gethostbyaddr((char *)a, sizeof(*a), AF_INET6); 119220802Sglebius if (he != NULL) /* resolved to name */ 120220802Sglebius printf("%s", he->h_name); 121220802Sglebius else if (mb == 0) /* any */ 122220802Sglebius printf("any"); 123220802Sglebius else { /* numeric IP followed by some kind of mask */ 124220802Sglebius if (inet_ntop(AF_INET6, a, trad, sizeof( trad ) ) == NULL) 125220802Sglebius printf("Error ntop in print_ip6\n"); 126220802Sglebius printf("%s", trad ); 127220802Sglebius if (mb < 0) /* XXX not really legal... */ 128220802Sglebius printf(":%s", 129220802Sglebius inet_ntop(AF_INET6, &a[1], trad, sizeof(trad))); 130220802Sglebius else if (mb < 128) 131220802Sglebius printf("/%d", mb); 132220802Sglebius } 133220802Sglebius if (len > 2) 134220802Sglebius printf(","); 135187770Sluigi } 136187770Sluigi} 137187770Sluigi 138187770Sluigivoid 139248505Smelifarofill_icmp6types(ipfw_insn_icmp6 *cmd, char *av, int cblen) 140187770Sluigi{ 141187770Sluigi uint8_t type; 142187770Sluigi 143248505Smelifaro CHECK_LENGTH(cblen, F_INSN_SIZE(ipfw_insn_icmp6)); 144248505Smelifaro 145187770Sluigi bzero(cmd, sizeof(*cmd)); 146187770Sluigi while (*av) { 147220802Sglebius if (*av == ',') 148220802Sglebius av++; 149220802Sglebius type = strtoul(av, &av, 0); 150220802Sglebius if (*av != ',' && *av != '\0') 151220802Sglebius errx(EX_DATAERR, "invalid ICMP6 type"); 152187770Sluigi /* 153187770Sluigi * XXX: shouldn't this be 0xFF? I can't see any reason why 154187770Sluigi * we shouldn't be able to filter all possiable values 155187770Sluigi * regardless of the ability of the rest of the kernel to do 156187770Sluigi * anything useful with them. 157187770Sluigi */ 158220802Sglebius if (type > ICMP6_MAXTYPE) 159220802Sglebius errx(EX_DATAERR, "ICMP6 type out of range"); 160220802Sglebius cmd->d[type / 32] |= ( 1 << (type % 32)); 161187770Sluigi } 162187770Sluigi cmd->o.opcode = O_ICMP6TYPE; 163187770Sluigi cmd->o.len |= F_INSN_SIZE(ipfw_insn_icmp6); 164187770Sluigi} 165187770Sluigi 166187770Sluigi 167187770Sluigivoid 168187770Sluigiprint_icmp6types(ipfw_insn_u32 *cmd) 169187770Sluigi{ 170187770Sluigi int i, j; 171187770Sluigi char sep= ' '; 172187770Sluigi 173187770Sluigi printf(" ip6 icmp6types"); 174187770Sluigi for (i = 0; i < 7; i++) 175220802Sglebius for (j=0; j < 32; ++j) { 176220802Sglebius if ( (cmd->d[i] & (1 << (j))) == 0) 177220802Sglebius continue; 178220802Sglebius printf("%c%d", sep, (i*32 + j)); 179220802Sglebius sep = ','; 180220802Sglebius } 181187770Sluigi} 182187770Sluigi 183187770Sluigivoid 184187770Sluigiprint_flow6id( ipfw_insn_u32 *cmd) 185187770Sluigi{ 186187770Sluigi uint16_t i, limit = cmd->o.arg1; 187187770Sluigi char sep = ','; 188187770Sluigi 189187770Sluigi printf(" flow-id "); 190187770Sluigi for( i=0; i < limit; ++i) { 191220802Sglebius if (i == limit - 1) 192220802Sglebius sep = ' '; 193220802Sglebius printf("%d%c", cmd->d[i], sep); 194187770Sluigi } 195187770Sluigi} 196187770Sluigi 197187770Sluigi/* structure and define for the extension header in ipv6 */ 198187770Sluigistatic struct _s_x ext6hdrcodes[] = { 199187770Sluigi { "frag", EXT_FRAGMENT }, 200187770Sluigi { "hopopt", EXT_HOPOPTS }, 201187770Sluigi { "route", EXT_ROUTING }, 202187770Sluigi { "dstopt", EXT_DSTOPTS }, 203220802Sglebius { "ah", EXT_AH }, 204220802Sglebius { "esp", EXT_ESP }, 205187770Sluigi { "rthdr0", EXT_RTHDR0 }, 206187770Sluigi { "rthdr2", EXT_RTHDR2 }, 207220802Sglebius { NULL, 0 } 208187770Sluigi}; 209187770Sluigi 210187770Sluigi/* fills command for the extension header filtering */ 211187770Sluigiint 212187770Sluigifill_ext6hdr( ipfw_insn *cmd, char *av) 213187770Sluigi{ 214187770Sluigi int tok; 215187770Sluigi char *s = av; 216187770Sluigi 217187770Sluigi cmd->arg1 = 0; 218187770Sluigi 219187770Sluigi while(s) { 220220802Sglebius av = strsep( &s, ",") ; 221220802Sglebius tok = match_token(ext6hdrcodes, av); 222220802Sglebius switch (tok) { 223220802Sglebius case EXT_FRAGMENT: 224220802Sglebius cmd->arg1 |= EXT_FRAGMENT; 225220802Sglebius break; 226187770Sluigi 227220802Sglebius case EXT_HOPOPTS: 228220802Sglebius cmd->arg1 |= EXT_HOPOPTS; 229220802Sglebius break; 230187770Sluigi 231220802Sglebius case EXT_ROUTING: 232220802Sglebius cmd->arg1 |= EXT_ROUTING; 233220802Sglebius break; 234187770Sluigi 235220802Sglebius case EXT_DSTOPTS: 236220802Sglebius cmd->arg1 |= EXT_DSTOPTS; 237220802Sglebius break; 238187770Sluigi 239220802Sglebius case EXT_AH: 240220802Sglebius cmd->arg1 |= EXT_AH; 241220802Sglebius break; 242187770Sluigi 243220802Sglebius case EXT_ESP: 244220802Sglebius cmd->arg1 |= EXT_ESP; 245220802Sglebius break; 246187770Sluigi 247220802Sglebius case EXT_RTHDR0: 248220802Sglebius cmd->arg1 |= EXT_RTHDR0; 249220802Sglebius break; 250187770Sluigi 251220802Sglebius case EXT_RTHDR2: 252220802Sglebius cmd->arg1 |= EXT_RTHDR2; 253220802Sglebius break; 254187770Sluigi 255220802Sglebius default: 256220802Sglebius errx( EX_DATAERR, "invalid option for ipv6 exten header" ); 257220802Sglebius break; 258220802Sglebius } 259187770Sluigi } 260187770Sluigi if (cmd->arg1 == 0 ) 261220802Sglebius return 0; 262187770Sluigi cmd->opcode = O_EXT_HDR; 263187770Sluigi cmd->len |= F_INSN_SIZE( ipfw_insn ); 264187770Sluigi return 1; 265187770Sluigi} 266187770Sluigi 267187770Sluigivoid 268187770Sluigiprint_ext6hdr( ipfw_insn *cmd ) 269187770Sluigi{ 270187770Sluigi char sep = ' '; 271187770Sluigi 272187770Sluigi printf(" extension header:"); 273187770Sluigi if (cmd->arg1 & EXT_FRAGMENT ) { 274220802Sglebius printf("%cfragmentation", sep); 275220802Sglebius sep = ','; 276187770Sluigi } 277187770Sluigi if (cmd->arg1 & EXT_HOPOPTS ) { 278220802Sglebius printf("%chop options", sep); 279220802Sglebius sep = ','; 280187770Sluigi } 281187770Sluigi if (cmd->arg1 & EXT_ROUTING ) { 282220802Sglebius printf("%crouting options", sep); 283220802Sglebius sep = ','; 284187770Sluigi } 285187770Sluigi if (cmd->arg1 & EXT_RTHDR0 ) { 286220802Sglebius printf("%crthdr0", sep); 287220802Sglebius sep = ','; 288187770Sluigi } 289187770Sluigi if (cmd->arg1 & EXT_RTHDR2 ) { 290220802Sglebius printf("%crthdr2", sep); 291220802Sglebius sep = ','; 292187770Sluigi } 293187770Sluigi if (cmd->arg1 & EXT_DSTOPTS ) { 294220802Sglebius printf("%cdestination options", sep); 295220802Sglebius sep = ','; 296187770Sluigi } 297187770Sluigi if (cmd->arg1 & EXT_AH ) { 298220802Sglebius printf("%cauthentication header", sep); 299220802Sglebius sep = ','; 300187770Sluigi } 301187770Sluigi if (cmd->arg1 & EXT_ESP ) { 302220802Sglebius printf("%cencapsulated security payload", sep); 303187770Sluigi } 304187770Sluigi} 305187770Sluigi 306187770Sluigi/* Try to find ipv6 address by hostname */ 307187770Sluigistatic int 308187770Sluigilookup_host6 (char *host, struct in6_addr *ip6addr) 309187770Sluigi{ 310187770Sluigi struct hostent *he; 311187770Sluigi 312187770Sluigi if (!inet_pton(AF_INET6, host, ip6addr)) { 313187770Sluigi if ((he = gethostbyname2(host, AF_INET6)) == NULL) 314187770Sluigi return(-1); 315187770Sluigi memcpy(ip6addr, he->h_addr_list[0], sizeof( struct in6_addr)); 316187770Sluigi } 317187770Sluigi return(0); 318187770Sluigi} 319187770Sluigi 320187770Sluigi 321187770Sluigi/* 322187770Sluigi * fill the addr and mask fields in the instruction as appropriate from av. 323187770Sluigi * Update length as appropriate. 324187770Sluigi * The following formats are allowed: 325187770Sluigi * any matches any IP6. Actually returns an empty instruction. 326187770Sluigi * me returns O_IP6_*_ME 327187770Sluigi * 328220802Sglebius * 03f1::234:123:0342 single IP6 addres 329220802Sglebius * 03f1::234:123:0342/24 address/mask 330220802Sglebius * 03f1::234:123:0342/24,03f1::234:123:0343/ List of address 331187770Sluigi * 332187770Sluigi * Set of address (as in ipv6) not supported because ipv6 address 333187770Sluigi * are typically random past the initial prefix. 334187770Sluigi * Return 1 on success, 0 on failure. 335187770Sluigi */ 336187770Sluigistatic int 337248505Smelifarofill_ip6(ipfw_insn_ip6 *cmd, char *av, int cblen) 338187770Sluigi{ 339187770Sluigi int len = 0; 340187770Sluigi struct in6_addr *d = &(cmd->addr6); 341187770Sluigi /* 342187770Sluigi * Needed for multiple address. 343187770Sluigi * Note d[1] points to struct in6_add r mask6 of cmd 344187770Sluigi */ 345187770Sluigi 346241883Smelifaro cmd->o.len &= ~F_LEN_MASK; /* zero len */ 347187770Sluigi 348241883Smelifaro if (strcmp(av, "any") == 0) 349241883Smelifaro return (1); 350187770Sluigi 351187770Sluigi 352241883Smelifaro if (strcmp(av, "me") == 0) { /* Set the data for "me" opt*/ 353241883Smelifaro cmd->o.len |= F_INSN_SIZE(ipfw_insn); 354241883Smelifaro return (1); 355241883Smelifaro } 356187770Sluigi 357241883Smelifaro if (strcmp(av, "me6") == 0) { /* Set the data for "me" opt*/ 358241883Smelifaro cmd->o.len |= F_INSN_SIZE(ipfw_insn); 359241883Smelifaro return (1); 360241883Smelifaro } 361187770Sluigi 362241883Smelifaro if (strncmp(av, "table(", 6) == 0) { 363241883Smelifaro char *p = strchr(av + 6, ','); 364241883Smelifaro uint32_t *dm = ((ipfw_insn_u32 *)cmd)->d; 365241883Smelifaro 366241883Smelifaro if (p) 367241883Smelifaro *p++ = '\0'; 368241883Smelifaro cmd->o.opcode = O_IP_DST_LOOKUP; 369241883Smelifaro cmd->o.arg1 = strtoul(av + 6, NULL, 0); 370241883Smelifaro if (p) { 371241883Smelifaro cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32); 372241883Smelifaro dm[0] = strtoul(p, NULL, 0); 373241883Smelifaro } else 374241883Smelifaro cmd->o.len |= F_INSN_SIZE(ipfw_insn); 375241883Smelifaro return (1); 376241883Smelifaro } 377241883Smelifaro 378241883Smelifaro av = strdup(av); 379241883Smelifaro while (av) { 380187770Sluigi /* 381187770Sluigi * After the address we can have '/' indicating a mask, 382187770Sluigi * or ',' indicating another address follows. 383187770Sluigi */ 384187770Sluigi 385187770Sluigi char *p; 386187770Sluigi int masklen; 387187770Sluigi char md = '\0'; 388187770Sluigi 389248505Smelifaro CHECK_LENGTH(cblen, 1 + len + 2 * F_INSN_SIZE(struct in6_addr)); 390248505Smelifaro 391187770Sluigi if ((p = strpbrk(av, "/,")) ) { 392187770Sluigi md = *p; /* save the separator */ 393187770Sluigi *p = '\0'; /* terminate address string */ 394187770Sluigi p++; /* and skip past it */ 395187770Sluigi } 396187770Sluigi /* now p points to NULL, mask or next entry */ 397187770Sluigi 398187770Sluigi /* lookup stores address in *d as a side effect */ 399187770Sluigi if (lookup_host6(av, d) != 0) { 400187770Sluigi /* XXX: failed. Free memory and go */ 401187770Sluigi errx(EX_DATAERR, "bad address \"%s\"", av); 402187770Sluigi } 403187770Sluigi /* next, look at the mask, if any */ 404187770Sluigi masklen = (md == '/') ? atoi(p) : 128; 405187770Sluigi if (masklen > 128 || masklen < 0) 406187770Sluigi errx(EX_DATAERR, "bad width \"%s\''", p); 407187770Sluigi else 408187770Sluigi n2mask(&d[1], masklen); 409187770Sluigi 410187770Sluigi APPLY_MASK(d, &d[1]) /* mask base address with mask */ 411187770Sluigi 412187770Sluigi /* find next separator */ 413187770Sluigi 414187770Sluigi if (md == '/') { /* find separator past the mask */ 415187770Sluigi p = strpbrk(p, ","); 416187770Sluigi if (p != NULL) 417187770Sluigi p++; 418187770Sluigi } 419187770Sluigi av = p; 420187770Sluigi 421187770Sluigi /* Check this entry */ 422187770Sluigi if (masklen == 0) { 423187770Sluigi /* 424187770Sluigi * 'any' turns the entire list into a NOP. 425187770Sluigi * 'not any' never matches, so it is removed from the 426187770Sluigi * list unless it is the only item, in which case we 427187770Sluigi * report an error. 428187770Sluigi */ 429187770Sluigi if (cmd->o.len & F_NOT && av == NULL && len == 0) 430187770Sluigi errx(EX_DATAERR, "not any never matches"); 431187770Sluigi continue; 432187770Sluigi } 433187770Sluigi 434187770Sluigi /* 435187770Sluigi * A single IP can be stored alone 436187770Sluigi */ 437187770Sluigi if (masklen == 128 && av == NULL && len == 0) { 438187770Sluigi len = F_INSN_SIZE(struct in6_addr); 439187770Sluigi break; 440187770Sluigi } 441187770Sluigi 442187770Sluigi /* Update length and pointer to arguments */ 443187770Sluigi len += F_INSN_SIZE(struct in6_addr)*2; 444187770Sluigi d += 2; 445187770Sluigi } /* end while */ 446187770Sluigi 447187770Sluigi /* 448187770Sluigi * Total length of the command, remember that 1 is the size of 449187770Sluigi * the base command. 450187770Sluigi */ 451187770Sluigi if (len + 1 > F_LEN_MASK) 452187770Sluigi errx(EX_DATAERR, "address list too long"); 453187770Sluigi cmd->o.len |= len+1; 454187770Sluigi free(av); 455187770Sluigi return (1); 456187770Sluigi} 457187770Sluigi 458187770Sluigi/* 459187770Sluigi * fills command for ipv6 flow-id filtering 460187770Sluigi * note that the 20 bit flow number is stored in a array of u_int32_t 461187770Sluigi * it's supported lists of flow-id, so in the o.arg1 we store how many 462187770Sluigi * additional flow-id we want to filter, the basic is 1 463187770Sluigi */ 464187770Sluigivoid 465248505Smelifarofill_flow6( ipfw_insn_u32 *cmd, char *av, int cblen) 466187770Sluigi{ 467187770Sluigi u_int32_t type; /* Current flow number */ 468187770Sluigi u_int16_t nflow = 0; /* Current flow index */ 469187770Sluigi char *s = av; 470187770Sluigi cmd->d[0] = 0; /* Initializing the base number*/ 471187770Sluigi 472187770Sluigi while (s) { 473248505Smelifaro CHECK_LENGTH(cblen, F_INSN_SIZE(ipfw_insn_u32) + nflow + 1); 474248505Smelifaro 475187770Sluigi av = strsep( &s, ",") ; 476187770Sluigi type = strtoul(av, &av, 0); 477187770Sluigi if (*av != ',' && *av != '\0') 478187770Sluigi errx(EX_DATAERR, "invalid ipv6 flow number %s", av); 479187770Sluigi if (type > 0xfffff) 480187770Sluigi errx(EX_DATAERR, "flow number out of range %s", av); 481187770Sluigi cmd->d[nflow] |= type; 482187770Sluigi nflow++; 483187770Sluigi } 484187770Sluigi if( nflow > 0 ) { 485187770Sluigi cmd->o.opcode = O_FLOW6ID; 486187770Sluigi cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + nflow; 487187770Sluigi cmd->o.arg1 = nflow; 488187770Sluigi } 489187770Sluigi else { 490187770Sluigi errx(EX_DATAERR, "invalid ipv6 flow number %s", av); 491187770Sluigi } 492187770Sluigi} 493187770Sluigi 494187770Sluigiipfw_insn * 495248505Smelifaroadd_srcip6(ipfw_insn *cmd, char *av, int cblen) 496187770Sluigi{ 497187770Sluigi 498248505Smelifaro fill_ip6((ipfw_insn_ip6 *)cmd, av, cblen); 499241883Smelifaro if (cmd->opcode == O_IP_DST_SET) /* set */ 500241883Smelifaro cmd->opcode = O_IP_SRC_SET; 501241883Smelifaro else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */ 502241883Smelifaro cmd->opcode = O_IP_SRC_LOOKUP; 503241883Smelifaro else if (F_LEN(cmd) == 0) { /* any */ 504187770Sluigi } else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) { /* "me" */ 505187770Sluigi cmd->opcode = O_IP6_SRC_ME; 506187770Sluigi } else if (F_LEN(cmd) == 507187770Sluigi (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) { 508187770Sluigi /* single IP, no mask*/ 509187770Sluigi cmd->opcode = O_IP6_SRC; 510187770Sluigi } else { /* addr/mask opt */ 511187770Sluigi cmd->opcode = O_IP6_SRC_MASK; 512187770Sluigi } 513187770Sluigi return cmd; 514187770Sluigi} 515187770Sluigi 516187770Sluigiipfw_insn * 517248505Smelifaroadd_dstip6(ipfw_insn *cmd, char *av, int cblen) 518187770Sluigi{ 519187770Sluigi 520248505Smelifaro fill_ip6((ipfw_insn_ip6 *)cmd, av, cblen); 521241883Smelifaro if (cmd->opcode == O_IP_DST_SET) /* set */ 522241883Smelifaro ; 523241883Smelifaro else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */ 524241883Smelifaro ; 525241883Smelifaro else if (F_LEN(cmd) == 0) { /* any */ 526187770Sluigi } else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) { /* "me" */ 527187770Sluigi cmd->opcode = O_IP6_DST_ME; 528187770Sluigi } else if (F_LEN(cmd) == 529187770Sluigi (F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) { 530187770Sluigi /* single IP, no mask*/ 531187770Sluigi cmd->opcode = O_IP6_DST; 532187770Sluigi } else { /* addr/mask opt */ 533187770Sluigi cmd->opcode = O_IP6_DST_MASK; 534187770Sluigi } 535187770Sluigi return cmd; 536187770Sluigi} 537