Put nat and ipv6 support in their own files.
Usual moving of code with no changes from ipfw2.c to the newly created files, and addition of prototypes to ipfw2.h I have added forward declarations for ipfw_insn_* in ipfw2.h to avoid a global dependency on ip_fw.h
This commit is contained in:
parent
5153c1f1c4
commit
8a3b5c8587
@ -1,7 +1,7 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PROG= ipfw
|
||||
SRCS= ipfw2.c dummynet.c main.c
|
||||
SRCS= ipfw2.c dummynet.c ipv6.c main.c nat.c
|
||||
WARNS?= 0
|
||||
MAN= ipfw.8
|
||||
|
||||
|
1319
sbin/ipfw/ipfw2.c
1319
sbin/ipfw/ipfw2.c
File diff suppressed because it is too large
Load Diff
@ -194,15 +194,25 @@ enum tokens {
|
||||
void *safe_calloc(size_t number, size_t size);
|
||||
void *safe_realloc(void *ptr, size_t size);
|
||||
|
||||
/* a string comparison function used for historical compatibility */
|
||||
/* string comparison functions used for historical compatibility */
|
||||
int _substrcmp(const char *str1, const char* str2);
|
||||
int _substrcmp2(const char *str1, const char* str2, const char* str3);
|
||||
|
||||
/* utility functions */
|
||||
int match_token(struct _s_x *table, char *string);
|
||||
char const *match_value(struct _s_x *p, int value);
|
||||
|
||||
int do_cmd(int optname, void *optval, uintptr_t optlen);
|
||||
|
||||
struct in6_addr;
|
||||
void n2mask(struct in6_addr *mask, int n);
|
||||
int contigmask(uint8_t *p, int len);
|
||||
|
||||
/* forward declarations to avoid header dependency */
|
||||
typedef struct _ipfw_insn ipfw_insn;
|
||||
typedef struct _ipfw_insn_u32 ipfw_insn_u32;
|
||||
typedef struct _ipfw_insn_ip6 ipfw_insn_ip6;
|
||||
typedef struct _ipfw_insn_icmp6 ipfw_insn_icmp6;
|
||||
|
||||
|
||||
/*
|
||||
@ -212,6 +222,7 @@ void n2mask(struct in6_addr *mask, int n);
|
||||
*/
|
||||
extern int resvd_set_number;
|
||||
|
||||
/* first-level command handlers */
|
||||
void ipfw_add(int ac, char *av[]);
|
||||
void ipfw_show_nat(int ac, char **av);
|
||||
void ipfw_config_pipe(int ac, char **av);
|
||||
@ -224,7 +235,21 @@ void ipfw_flush(int force);
|
||||
void ipfw_zero(int ac, char *av[], int optname);
|
||||
void ipfw_list(int ac, char *av[], int show_counters);
|
||||
|
||||
/* in dummynet.c */
|
||||
/* dummynet.c */
|
||||
void ipfw_list_pipes(void *data, uint nbytes, int ac, char *av[]);
|
||||
int ipfw_delete_pipe(int pipe_or_queue, int n);
|
||||
|
||||
/* ipv6.c */
|
||||
void print_unreach6_code(uint16_t code);
|
||||
void print_ip6(ipfw_insn_ip6 *cmd, char const *s);
|
||||
void print_flow6id( ipfw_insn_u32 *cmd);
|
||||
void print_icmp6types(ipfw_insn_u32 *cmd);
|
||||
void print_ext6hdr( ipfw_insn *cmd );
|
||||
|
||||
ipfw_insn *add_srcip6(ipfw_insn *cmd, char *av);
|
||||
ipfw_insn *add_dstip6(ipfw_insn *cmd, char *av);
|
||||
|
||||
void fill_flow6( ipfw_insn_u32 *cmd, char *av );
|
||||
void fill_unreach6_code(u_short *codep, char *str);
|
||||
void fill_icmp6types(ipfw_insn_icmp6 *cmd, char *av);
|
||||
int fill_ext6hdr( ipfw_insn *cmd, char *av);
|
||||
|
501
sbin/ipfw/ipv6.c
Normal file
501
sbin/ipfw/ipv6.c
Normal file
@ -0,0 +1,501 @@
|
||||
/*
|
||||
* Copyright (c) 2002-2003 Luigi Rizzo
|
||||
* Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
|
||||
* Copyright (c) 1994 Ugen J.S.Antsilevich
|
||||
*
|
||||
* Idea and grammar partially left from:
|
||||
* Copyright (c) 1993 Daniel Boulet
|
||||
*
|
||||
* Redistribution and use in source forms, with and without modification,
|
||||
* are permitted provided that this entire comment appears intact.
|
||||
*
|
||||
* Redistribution in binary form may occur without any restrictions.
|
||||
* Obviously, it would be nice if you gave credit where credit is due
|
||||
* but requiring it would be too onerous.
|
||||
*
|
||||
* This software is provided ``AS IS'' without any warranties of any kind.
|
||||
*
|
||||
* NEW command line interface for IP firewall facility
|
||||
*
|
||||
* $FreeBSD$
|
||||
*
|
||||
* ipv6 support
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "ipfw2.h"
|
||||
|
||||
#include <err.h>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/in_systm.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/icmp6.h>
|
||||
#include <netinet/ip_fw.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
static struct _s_x icmp6codes[] = {
|
||||
{ "no-route", ICMP6_DST_UNREACH_NOROUTE },
|
||||
{ "admin-prohib", ICMP6_DST_UNREACH_ADMIN },
|
||||
{ "address", ICMP6_DST_UNREACH_ADDR },
|
||||
{ "port", ICMP6_DST_UNREACH_NOPORT },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
void
|
||||
fill_unreach6_code(u_short *codep, char *str)
|
||||
{
|
||||
int val;
|
||||
char *s;
|
||||
|
||||
val = strtoul(str, &s, 0);
|
||||
if (s == str || *s != '\0' || val >= 0x100)
|
||||
val = match_token(icmp6codes, str);
|
||||
if (val < 0)
|
||||
errx(EX_DATAERR, "unknown ICMPv6 unreachable code ``%s''", str);
|
||||
*codep = val;
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
print_unreach6_code(uint16_t code)
|
||||
{
|
||||
char const *s = match_value(icmp6codes, code);
|
||||
|
||||
if (s != NULL)
|
||||
printf("unreach6 %s", s);
|
||||
else
|
||||
printf("unreach6 %u", code);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print the ip address contained in a command.
|
||||
*/
|
||||
void
|
||||
print_ip6(ipfw_insn_ip6 *cmd, char const *s)
|
||||
{
|
||||
struct hostent *he = NULL;
|
||||
int len = F_LEN((ipfw_insn *) cmd) - 1;
|
||||
struct in6_addr *a = &(cmd->addr6);
|
||||
char trad[255];
|
||||
|
||||
printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s);
|
||||
|
||||
if (cmd->o.opcode == O_IP6_SRC_ME || cmd->o.opcode == O_IP6_DST_ME) {
|
||||
printf("me6");
|
||||
return;
|
||||
}
|
||||
if (cmd->o.opcode == O_IP6) {
|
||||
printf(" ip6");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* len == 4 indicates a single IP, whereas lists of 1 or more
|
||||
* addr/mask pairs have len = (2n+1). We convert len to n so we
|
||||
* use that to count the number of entries.
|
||||
*/
|
||||
|
||||
for (len = len / 4; len > 0; len -= 2, a += 2) {
|
||||
int mb = /* mask length */
|
||||
(cmd->o.opcode == O_IP6_SRC || cmd->o.opcode == O_IP6_DST) ?
|
||||
128 : contigmask((uint8_t *)&(a[1]), 128);
|
||||
|
||||
if (mb == 128 && co.do_resolv)
|
||||
he = gethostbyaddr((char *)a, sizeof(*a), AF_INET6);
|
||||
if (he != NULL) /* resolved to name */
|
||||
printf("%s", he->h_name);
|
||||
else if (mb == 0) /* any */
|
||||
printf("any");
|
||||
else { /* numeric IP followed by some kind of mask */
|
||||
if (inet_ntop(AF_INET6, a, trad, sizeof( trad ) ) == NULL)
|
||||
printf("Error ntop in print_ip6\n");
|
||||
printf("%s", trad );
|
||||
if (mb < 0) /* XXX not really legal... */
|
||||
printf(":%s",
|
||||
inet_ntop(AF_INET6, &a[1], trad, sizeof(trad)));
|
||||
else if (mb < 128)
|
||||
printf("/%d", mb);
|
||||
}
|
||||
if (len > 2)
|
||||
printf(",");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
fill_icmp6types(ipfw_insn_icmp6 *cmd, char *av)
|
||||
{
|
||||
uint8_t type;
|
||||
|
||||
bzero(cmd, sizeof(*cmd));
|
||||
while (*av) {
|
||||
if (*av == ',')
|
||||
av++;
|
||||
type = strtoul(av, &av, 0);
|
||||
if (*av != ',' && *av != '\0')
|
||||
errx(EX_DATAERR, "invalid ICMP6 type");
|
||||
/*
|
||||
* XXX: shouldn't this be 0xFF? I can't see any reason why
|
||||
* we shouldn't be able to filter all possiable values
|
||||
* regardless of the ability of the rest of the kernel to do
|
||||
* anything useful with them.
|
||||
*/
|
||||
if (type > ICMP6_MAXTYPE)
|
||||
errx(EX_DATAERR, "ICMP6 type out of range");
|
||||
cmd->d[type / 32] |= ( 1 << (type % 32));
|
||||
}
|
||||
cmd->o.opcode = O_ICMP6TYPE;
|
||||
cmd->o.len |= F_INSN_SIZE(ipfw_insn_icmp6);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
print_icmp6types(ipfw_insn_u32 *cmd)
|
||||
{
|
||||
int i, j;
|
||||
char sep= ' ';
|
||||
|
||||
printf(" ip6 icmp6types");
|
||||
for (i = 0; i < 7; i++)
|
||||
for (j=0; j < 32; ++j) {
|
||||
if ( (cmd->d[i] & (1 << (j))) == 0)
|
||||
continue;
|
||||
printf("%c%d", sep, (i*32 + j));
|
||||
sep = ',';
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
print_flow6id( ipfw_insn_u32 *cmd)
|
||||
{
|
||||
uint16_t i, limit = cmd->o.arg1;
|
||||
char sep = ',';
|
||||
|
||||
printf(" flow-id ");
|
||||
for( i=0; i < limit; ++i) {
|
||||
if (i == limit - 1)
|
||||
sep = ' ';
|
||||
printf("%d%c", cmd->d[i], sep);
|
||||
}
|
||||
}
|
||||
|
||||
/* structure and define for the extension header in ipv6 */
|
||||
static struct _s_x ext6hdrcodes[] = {
|
||||
{ "frag", EXT_FRAGMENT },
|
||||
{ "hopopt", EXT_HOPOPTS },
|
||||
{ "route", EXT_ROUTING },
|
||||
{ "dstopt", EXT_DSTOPTS },
|
||||
{ "ah", EXT_AH },
|
||||
{ "esp", EXT_ESP },
|
||||
{ "rthdr0", EXT_RTHDR0 },
|
||||
{ "rthdr2", EXT_RTHDR2 },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
/* fills command for the extension header filtering */
|
||||
int
|
||||
fill_ext6hdr( ipfw_insn *cmd, char *av)
|
||||
{
|
||||
int tok;
|
||||
char *s = av;
|
||||
|
||||
cmd->arg1 = 0;
|
||||
|
||||
while(s) {
|
||||
av = strsep( &s, ",") ;
|
||||
tok = match_token(ext6hdrcodes, av);
|
||||
switch (tok) {
|
||||
case EXT_FRAGMENT:
|
||||
cmd->arg1 |= EXT_FRAGMENT;
|
||||
break;
|
||||
|
||||
case EXT_HOPOPTS:
|
||||
cmd->arg1 |= EXT_HOPOPTS;
|
||||
break;
|
||||
|
||||
case EXT_ROUTING:
|
||||
cmd->arg1 |= EXT_ROUTING;
|
||||
break;
|
||||
|
||||
case EXT_DSTOPTS:
|
||||
cmd->arg1 |= EXT_DSTOPTS;
|
||||
break;
|
||||
|
||||
case EXT_AH:
|
||||
cmd->arg1 |= EXT_AH;
|
||||
break;
|
||||
|
||||
case EXT_ESP:
|
||||
cmd->arg1 |= EXT_ESP;
|
||||
break;
|
||||
|
||||
case EXT_RTHDR0:
|
||||
cmd->arg1 |= EXT_RTHDR0;
|
||||
break;
|
||||
|
||||
case EXT_RTHDR2:
|
||||
cmd->arg1 |= EXT_RTHDR2;
|
||||
break;
|
||||
|
||||
default:
|
||||
errx( EX_DATAERR, "invalid option for ipv6 exten header" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cmd->arg1 == 0 )
|
||||
return 0;
|
||||
cmd->opcode = O_EXT_HDR;
|
||||
cmd->len |= F_INSN_SIZE( ipfw_insn );
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
print_ext6hdr( ipfw_insn *cmd )
|
||||
{
|
||||
char sep = ' ';
|
||||
|
||||
printf(" extension header:");
|
||||
if (cmd->arg1 & EXT_FRAGMENT ) {
|
||||
printf("%cfragmentation", sep);
|
||||
sep = ',';
|
||||
}
|
||||
if (cmd->arg1 & EXT_HOPOPTS ) {
|
||||
printf("%chop options", sep);
|
||||
sep = ',';
|
||||
}
|
||||
if (cmd->arg1 & EXT_ROUTING ) {
|
||||
printf("%crouting options", sep);
|
||||
sep = ',';
|
||||
}
|
||||
if (cmd->arg1 & EXT_RTHDR0 ) {
|
||||
printf("%crthdr0", sep);
|
||||
sep = ',';
|
||||
}
|
||||
if (cmd->arg1 & EXT_RTHDR2 ) {
|
||||
printf("%crthdr2", sep);
|
||||
sep = ',';
|
||||
}
|
||||
if (cmd->arg1 & EXT_DSTOPTS ) {
|
||||
printf("%cdestination options", sep);
|
||||
sep = ',';
|
||||
}
|
||||
if (cmd->arg1 & EXT_AH ) {
|
||||
printf("%cauthentication header", sep);
|
||||
sep = ',';
|
||||
}
|
||||
if (cmd->arg1 & EXT_ESP ) {
|
||||
printf("%cencapsulated security payload", sep);
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to find ipv6 address by hostname */
|
||||
static int
|
||||
lookup_host6 (char *host, struct in6_addr *ip6addr)
|
||||
{
|
||||
struct hostent *he;
|
||||
|
||||
if (!inet_pton(AF_INET6, host, ip6addr)) {
|
||||
if ((he = gethostbyname2(host, AF_INET6)) == NULL)
|
||||
return(-1);
|
||||
memcpy(ip6addr, he->h_addr_list[0], sizeof( struct in6_addr));
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* fill the addr and mask fields in the instruction as appropriate from av.
|
||||
* Update length as appropriate.
|
||||
* The following formats are allowed:
|
||||
* any matches any IP6. Actually returns an empty instruction.
|
||||
* me returns O_IP6_*_ME
|
||||
*
|
||||
* 03f1::234:123:0342 single IP6 addres
|
||||
* 03f1::234:123:0342/24 address/mask
|
||||
* 03f1::234:123:0342/24,03f1::234:123:0343/ List of address
|
||||
*
|
||||
* Set of address (as in ipv6) not supported because ipv6 address
|
||||
* are typically random past the initial prefix.
|
||||
* Return 1 on success, 0 on failure.
|
||||
*/
|
||||
static int
|
||||
fill_ip6(ipfw_insn_ip6 *cmd, char *av)
|
||||
{
|
||||
int len = 0;
|
||||
struct in6_addr *d = &(cmd->addr6);
|
||||
/*
|
||||
* Needed for multiple address.
|
||||
* Note d[1] points to struct in6_add r mask6 of cmd
|
||||
*/
|
||||
|
||||
cmd->o.len &= ~F_LEN_MASK; /* zero len */
|
||||
|
||||
if (strcmp(av, "any") == 0)
|
||||
return (1);
|
||||
|
||||
|
||||
if (strcmp(av, "me") == 0) { /* Set the data for "me" opt*/
|
||||
cmd->o.len |= F_INSN_SIZE(ipfw_insn);
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (strcmp(av, "me6") == 0) { /* Set the data for "me" opt*/
|
||||
cmd->o.len |= F_INSN_SIZE(ipfw_insn);
|
||||
return (1);
|
||||
}
|
||||
|
||||
av = strdup(av);
|
||||
while (av) {
|
||||
/*
|
||||
* After the address we can have '/' indicating a mask,
|
||||
* or ',' indicating another address follows.
|
||||
*/
|
||||
|
||||
char *p;
|
||||
int masklen;
|
||||
char md = '\0';
|
||||
|
||||
if ((p = strpbrk(av, "/,")) ) {
|
||||
md = *p; /* save the separator */
|
||||
*p = '\0'; /* terminate address string */
|
||||
p++; /* and skip past it */
|
||||
}
|
||||
/* now p points to NULL, mask or next entry */
|
||||
|
||||
/* lookup stores address in *d as a side effect */
|
||||
if (lookup_host6(av, d) != 0) {
|
||||
/* XXX: failed. Free memory and go */
|
||||
errx(EX_DATAERR, "bad address \"%s\"", av);
|
||||
}
|
||||
/* next, look at the mask, if any */
|
||||
masklen = (md == '/') ? atoi(p) : 128;
|
||||
if (masklen > 128 || masklen < 0)
|
||||
errx(EX_DATAERR, "bad width \"%s\''", p);
|
||||
else
|
||||
n2mask(&d[1], masklen);
|
||||
|
||||
APPLY_MASK(d, &d[1]) /* mask base address with mask */
|
||||
|
||||
/* find next separator */
|
||||
|
||||
if (md == '/') { /* find separator past the mask */
|
||||
p = strpbrk(p, ",");
|
||||
if (p != NULL)
|
||||
p++;
|
||||
}
|
||||
av = p;
|
||||
|
||||
/* Check this entry */
|
||||
if (masklen == 0) {
|
||||
/*
|
||||
* 'any' turns the entire list into a NOP.
|
||||
* 'not any' never matches, so it is removed from the
|
||||
* list unless it is the only item, in which case we
|
||||
* report an error.
|
||||
*/
|
||||
if (cmd->o.len & F_NOT && av == NULL && len == 0)
|
||||
errx(EX_DATAERR, "not any never matches");
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* A single IP can be stored alone
|
||||
*/
|
||||
if (masklen == 128 && av == NULL && len == 0) {
|
||||
len = F_INSN_SIZE(struct in6_addr);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Update length and pointer to arguments */
|
||||
len += F_INSN_SIZE(struct in6_addr)*2;
|
||||
d += 2;
|
||||
} /* end while */
|
||||
|
||||
/*
|
||||
* Total length of the command, remember that 1 is the size of
|
||||
* the base command.
|
||||
*/
|
||||
if (len + 1 > F_LEN_MASK)
|
||||
errx(EX_DATAERR, "address list too long");
|
||||
cmd->o.len |= len+1;
|
||||
free(av);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* fills command for ipv6 flow-id filtering
|
||||
* note that the 20 bit flow number is stored in a array of u_int32_t
|
||||
* it's supported lists of flow-id, so in the o.arg1 we store how many
|
||||
* additional flow-id we want to filter, the basic is 1
|
||||
*/
|
||||
void
|
||||
fill_flow6( ipfw_insn_u32 *cmd, char *av )
|
||||
{
|
||||
u_int32_t type; /* Current flow number */
|
||||
u_int16_t nflow = 0; /* Current flow index */
|
||||
char *s = av;
|
||||
cmd->d[0] = 0; /* Initializing the base number*/
|
||||
|
||||
while (s) {
|
||||
av = strsep( &s, ",") ;
|
||||
type = strtoul(av, &av, 0);
|
||||
if (*av != ',' && *av != '\0')
|
||||
errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
|
||||
if (type > 0xfffff)
|
||||
errx(EX_DATAERR, "flow number out of range %s", av);
|
||||
cmd->d[nflow] |= type;
|
||||
nflow++;
|
||||
}
|
||||
if( nflow > 0 ) {
|
||||
cmd->o.opcode = O_FLOW6ID;
|
||||
cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + nflow;
|
||||
cmd->o.arg1 = nflow;
|
||||
}
|
||||
else {
|
||||
errx(EX_DATAERR, "invalid ipv6 flow number %s", av);
|
||||
}
|
||||
}
|
||||
|
||||
ipfw_insn *
|
||||
add_srcip6(ipfw_insn *cmd, char *av)
|
||||
{
|
||||
|
||||
fill_ip6((ipfw_insn_ip6 *)cmd, av);
|
||||
if (F_LEN(cmd) == 0) { /* any */
|
||||
} else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) { /* "me" */
|
||||
cmd->opcode = O_IP6_SRC_ME;
|
||||
} else if (F_LEN(cmd) ==
|
||||
(F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) {
|
||||
/* single IP, no mask*/
|
||||
cmd->opcode = O_IP6_SRC;
|
||||
} else { /* addr/mask opt */
|
||||
cmd->opcode = O_IP6_SRC_MASK;
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
ipfw_insn *
|
||||
add_dstip6(ipfw_insn *cmd, char *av)
|
||||
{
|
||||
|
||||
fill_ip6((ipfw_insn_ip6 *)cmd, av);
|
||||
if (F_LEN(cmd) == 0) { /* any */
|
||||
} else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) { /* "me" */
|
||||
cmd->opcode = O_IP6_DST_ME;
|
||||
} else if (F_LEN(cmd) ==
|
||||
(F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn))) {
|
||||
/* single IP, no mask*/
|
||||
cmd->opcode = O_IP6_DST;
|
||||
} else { /* addr/mask opt */
|
||||
cmd->opcode = O_IP6_DST_MASK;
|
||||
}
|
||||
return cmd;
|
||||
}
|
905
sbin/ipfw/nat.c
Normal file
905
sbin/ipfw/nat.c
Normal file
@ -0,0 +1,905 @@
|
||||
/*
|
||||
* Copyright (c) 2002-2003 Luigi Rizzo
|
||||
* Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
|
||||
* Copyright (c) 1994 Ugen J.S.Antsilevich
|
||||
*
|
||||
* Idea and grammar partially left from:
|
||||
* Copyright (c) 1993 Daniel Boulet
|
||||
*
|
||||
* Redistribution and use in source forms, with and without modification,
|
||||
* are permitted provided that this entire comment appears intact.
|
||||
*
|
||||
* Redistribution in binary form may occur without any restrictions.
|
||||
* Obviously, it would be nice if you gave credit where credit is due
|
||||
* but requiring it would be too onerous.
|
||||
*
|
||||
* This software is provided ``AS IS'' without any warranties of any kind.
|
||||
*
|
||||
* NEW command line interface for IP firewall facility
|
||||
*
|
||||
* $FreeBSD$
|
||||
*
|
||||
* In-kernel nat support
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sockio.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include "ipfw2.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
|
||||
#define IPFW_INTERNAL /* Access to protected structures in ip_fw.h. */
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/if_dl.h>
|
||||
#include <net/route.h> /* def. of struct route */
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/in_systm.h>
|
||||
#include <netinet/ip_fw.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <alias.h>
|
||||
|
||||
static struct _s_x nat_params[] = {
|
||||
{ "ip", TOK_IP },
|
||||
{ "if", TOK_IF },
|
||||
{ "log", TOK_ALOG },
|
||||
{ "deny_in", TOK_DENY_INC },
|
||||
{ "same_ports", TOK_SAME_PORTS },
|
||||
{ "unreg_only", TOK_UNREG_ONLY },
|
||||
{ "reset", TOK_RESET_ADDR },
|
||||
{ "reverse", TOK_ALIAS_REV },
|
||||
{ "proxy_only", TOK_PROXY_ONLY },
|
||||
{ "redirect_addr", TOK_REDIR_ADDR },
|
||||
{ "redirect_port", TOK_REDIR_PORT },
|
||||
{ "redirect_proto", TOK_REDIR_PROTO },
|
||||
{ NULL, 0 } /* terminator */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Search for interface with name "ifn", and fill n accordingly:
|
||||
*
|
||||
* n->ip ip address of interface "ifn"
|
||||
* n->if_name copy of interface name "ifn"
|
||||
*/
|
||||
static void
|
||||
set_addr_dynamic(const char *ifn, struct cfg_nat *n)
|
||||
{
|
||||
size_t needed;
|
||||
int mib[6];
|
||||
char *buf, *lim, *next;
|
||||
struct if_msghdr *ifm;
|
||||
struct ifa_msghdr *ifam;
|
||||
struct sockaddr_dl *sdl;
|
||||
struct sockaddr_in *sin;
|
||||
int ifIndex, ifMTU;
|
||||
|
||||
mib[0] = CTL_NET;
|
||||
mib[1] = PF_ROUTE;
|
||||
mib[2] = 0;
|
||||
mib[3] = AF_INET;
|
||||
mib[4] = NET_RT_IFLIST;
|
||||
mib[5] = 0;
|
||||
/*
|
||||
* Get interface data.
|
||||
*/
|
||||
if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
|
||||
err(1, "iflist-sysctl-estimate");
|
||||
buf = safe_calloc(1, needed);
|
||||
if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1)
|
||||
err(1, "iflist-sysctl-get");
|
||||
lim = buf + needed;
|
||||
/*
|
||||
* Loop through interfaces until one with
|
||||
* given name is found. This is done to
|
||||
* find correct interface index for routing
|
||||
* message processing.
|
||||
*/
|
||||
ifIndex = 0;
|
||||
next = buf;
|
||||
while (next < lim) {
|
||||
ifm = (struct if_msghdr *)next;
|
||||
next += ifm->ifm_msglen;
|
||||
if (ifm->ifm_version != RTM_VERSION) {
|
||||
if (co.verbose)
|
||||
warnx("routing message version %d "
|
||||
"not understood", ifm->ifm_version);
|
||||
continue;
|
||||
}
|
||||
if (ifm->ifm_type == RTM_IFINFO) {
|
||||
sdl = (struct sockaddr_dl *)(ifm + 1);
|
||||
if (strlen(ifn) == sdl->sdl_nlen &&
|
||||
strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) {
|
||||
ifIndex = ifm->ifm_index;
|
||||
ifMTU = ifm->ifm_data.ifi_mtu;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ifIndex)
|
||||
errx(1, "unknown interface name %s", ifn);
|
||||
/*
|
||||
* Get interface address.
|
||||
*/
|
||||
sin = NULL;
|
||||
while (next < lim) {
|
||||
ifam = (struct ifa_msghdr *)next;
|
||||
next += ifam->ifam_msglen;
|
||||
if (ifam->ifam_version != RTM_VERSION) {
|
||||
if (co.verbose)
|
||||
warnx("routing message version %d "
|
||||
"not understood", ifam->ifam_version);
|
||||
continue;
|
||||
}
|
||||
if (ifam->ifam_type != RTM_NEWADDR)
|
||||
break;
|
||||
if (ifam->ifam_addrs & RTA_IFA) {
|
||||
int i;
|
||||
char *cp = (char *)(ifam + 1);
|
||||
|
||||
for (i = 1; i < RTA_IFA; i <<= 1) {
|
||||
if (ifam->ifam_addrs & i)
|
||||
cp += SA_SIZE((struct sockaddr *)cp);
|
||||
}
|
||||
if (((struct sockaddr *)cp)->sa_family == AF_INET) {
|
||||
sin = (struct sockaddr_in *)cp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sin == NULL)
|
||||
errx(1, "%s: cannot get interface address", ifn);
|
||||
|
||||
n->ip = sin->sin_addr;
|
||||
strncpy(n->if_name, ifn, IF_NAMESIZE);
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX - The following functions, macros and definitions come from natd.c:
|
||||
* it would be better to move them outside natd.c, in a file
|
||||
* (redirect_support.[ch]?) shared by ipfw and natd, but for now i can live
|
||||
* with it.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Definition of a port range, and macros to deal with values.
|
||||
* FORMAT: HI 16-bits == first port in range, 0 == all ports.
|
||||
* LO 16-bits == number of ports in range
|
||||
* NOTES: - Port values are not stored in network byte order.
|
||||
*/
|
||||
|
||||
#define port_range u_long
|
||||
|
||||
#define GETLOPORT(x) ((x) >> 0x10)
|
||||
#define GETNUMPORTS(x) ((x) & 0x0000ffff)
|
||||
#define GETHIPORT(x) (GETLOPORT((x)) + GETNUMPORTS((x)))
|
||||
|
||||
/* Set y to be the low-port value in port_range variable x. */
|
||||
#define SETLOPORT(x,y) ((x) = ((x) & 0x0000ffff) | ((y) << 0x10))
|
||||
|
||||
/* Set y to be the number of ports in port_range variable x. */
|
||||
#define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y))
|
||||
|
||||
static void
|
||||
StrToAddr (const char* str, struct in_addr* addr)
|
||||
{
|
||||
struct hostent* hp;
|
||||
|
||||
if (inet_aton (str, addr))
|
||||
return;
|
||||
|
||||
hp = gethostbyname (str);
|
||||
if (!hp)
|
||||
errx (1, "unknown host %s", str);
|
||||
|
||||
memcpy (addr, hp->h_addr, sizeof (struct in_addr));
|
||||
}
|
||||
|
||||
static int
|
||||
StrToPortRange (const char* str, const char* proto, port_range *portRange)
|
||||
{
|
||||
char* sep;
|
||||
struct servent* sp;
|
||||
char* end;
|
||||
u_short loPort;
|
||||
u_short hiPort;
|
||||
|
||||
/* First see if this is a service, return corresponding port if so. */
|
||||
sp = getservbyname (str,proto);
|
||||
if (sp) {
|
||||
SETLOPORT(*portRange, ntohs(sp->s_port));
|
||||
SETNUMPORTS(*portRange, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Not a service, see if it's a single port or port range. */
|
||||
sep = strchr (str, '-');
|
||||
if (sep == NULL) {
|
||||
SETLOPORT(*portRange, strtol(str, &end, 10));
|
||||
if (end != str) {
|
||||
/* Single port. */
|
||||
SETNUMPORTS(*portRange, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Error in port range field. */
|
||||
errx (EX_DATAERR, "%s/%s: unknown service", str, proto);
|
||||
}
|
||||
|
||||
/* Port range, get the values and sanity check. */
|
||||
sscanf (str, "%hu-%hu", &loPort, &hiPort);
|
||||
SETLOPORT(*portRange, loPort);
|
||||
SETNUMPORTS(*portRange, 0); /* Error by default */
|
||||
if (loPort <= hiPort)
|
||||
SETNUMPORTS(*portRange, hiPort - loPort + 1);
|
||||
|
||||
if (GETNUMPORTS(*portRange) == 0)
|
||||
errx (EX_DATAERR, "invalid port range %s", str);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
StrToProto (const char* str)
|
||||
{
|
||||
if (!strcmp (str, "tcp"))
|
||||
return IPPROTO_TCP;
|
||||
|
||||
if (!strcmp (str, "udp"))
|
||||
return IPPROTO_UDP;
|
||||
|
||||
errx (EX_DATAERR, "unknown protocol %s. Expected tcp or udp", str);
|
||||
}
|
||||
|
||||
static int
|
||||
StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto,
|
||||
port_range *portRange)
|
||||
{
|
||||
char* ptr;
|
||||
|
||||
ptr = strchr (str, ':');
|
||||
if (!ptr)
|
||||
errx (EX_DATAERR, "%s is missing port number", str);
|
||||
|
||||
*ptr = '\0';
|
||||
++ptr;
|
||||
|
||||
StrToAddr (str, addr);
|
||||
return StrToPortRange (ptr, proto, portRange);
|
||||
}
|
||||
|
||||
/* End of stuff taken from natd.c. */
|
||||
|
||||
#define INC_ARGCV() do { \
|
||||
(*_av)++; \
|
||||
(*_ac)--; \
|
||||
av = *_av; \
|
||||
ac = *_ac; \
|
||||
} while(0)
|
||||
|
||||
/*
|
||||
* The next 3 functions add support for the addr, port and proto redirect and
|
||||
* their logic is loosely based on SetupAddressRedirect(), SetupPortRedirect()
|
||||
* and SetupProtoRedirect() from natd.c.
|
||||
*
|
||||
* Every setup_* function fills at least one redirect entry
|
||||
* (struct cfg_redir) and zero or more server pool entry (struct cfg_spool)
|
||||
* in buf.
|
||||
*
|
||||
* The format of data in buf is:
|
||||
*
|
||||
*
|
||||
* cfg_nat cfg_redir cfg_spool ...... cfg_spool
|
||||
*
|
||||
* ------------------------------------- ------------
|
||||
* | | .....X ... | | | | .....
|
||||
* ------------------------------------- ...... ------------
|
||||
* ^
|
||||
* spool_cnt n=0 ...... n=(X-1)
|
||||
*
|
||||
* len points to the amount of available space in buf
|
||||
* space counts the memory consumed by every function
|
||||
*
|
||||
* XXX - Every function get all the argv params so it
|
||||
* has to check, in optional parameters, that the next
|
||||
* args is a valid option for the redir entry and not
|
||||
* another token. Only redir_port and redir_proto are
|
||||
* affected by this.
|
||||
*/
|
||||
|
||||
static int
|
||||
setup_redir_addr(char *spool_buf, int len,
|
||||
int *_ac, char ***_av)
|
||||
{
|
||||
char **av, *sep; /* Token separator. */
|
||||
/* Temporary buffer used to hold server pool ip's. */
|
||||
char tmp_spool_buf[NAT_BUF_LEN];
|
||||
int ac, space, lsnat;
|
||||
struct cfg_redir *r;
|
||||
struct cfg_spool *tmp;
|
||||
|
||||
av = *_av;
|
||||
ac = *_ac;
|
||||
space = 0;
|
||||
lsnat = 0;
|
||||
if (len >= SOF_REDIR) {
|
||||
r = (struct cfg_redir *)spool_buf;
|
||||
/* Skip cfg_redir at beginning of buf. */
|
||||
spool_buf = &spool_buf[SOF_REDIR];
|
||||
space = SOF_REDIR;
|
||||
len -= SOF_REDIR;
|
||||
} else
|
||||
goto nospace;
|
||||
r->mode = REDIR_ADDR;
|
||||
/* Extract local address. */
|
||||
if (ac == 0)
|
||||
errx(EX_DATAERR, "redirect_addr: missing local address");
|
||||
sep = strchr(*av, ',');
|
||||
if (sep) { /* LSNAT redirection syntax. */
|
||||
r->laddr.s_addr = INADDR_NONE;
|
||||
/* Preserve av, copy spool servers to tmp_spool_buf. */
|
||||
strncpy(tmp_spool_buf, *av, strlen(*av)+1);
|
||||
lsnat = 1;
|
||||
} else
|
||||
StrToAddr(*av, &r->laddr);
|
||||
INC_ARGCV();
|
||||
|
||||
/* Extract public address. */
|
||||
if (ac == 0)
|
||||
errx(EX_DATAERR, "redirect_addr: missing public address");
|
||||
StrToAddr(*av, &r->paddr);
|
||||
INC_ARGCV();
|
||||
|
||||
/* Setup LSNAT server pool. */
|
||||
if (sep) {
|
||||
sep = strtok(tmp_spool_buf, ",");
|
||||
while (sep != NULL) {
|
||||
tmp = (struct cfg_spool *)spool_buf;
|
||||
if (len < SOF_SPOOL)
|
||||
goto nospace;
|
||||
len -= SOF_SPOOL;
|
||||
space += SOF_SPOOL;
|
||||
StrToAddr(sep, &tmp->addr);
|
||||
tmp->port = ~0;
|
||||
r->spool_cnt++;
|
||||
/* Point to the next possible cfg_spool. */
|
||||
spool_buf = &spool_buf[SOF_SPOOL];
|
||||
sep = strtok(NULL, ",");
|
||||
}
|
||||
}
|
||||
return(space);
|
||||
nospace:
|
||||
errx(EX_DATAERR, "redirect_addr: buf is too small\n");
|
||||
}
|
||||
|
||||
static int
|
||||
setup_redir_port(char *spool_buf, int len,
|
||||
int *_ac, char ***_av)
|
||||
{
|
||||
char **av, *sep, *protoName;
|
||||
char tmp_spool_buf[NAT_BUF_LEN];
|
||||
int ac, space, lsnat;
|
||||
struct cfg_redir *r;
|
||||
struct cfg_spool *tmp;
|
||||
u_short numLocalPorts;
|
||||
port_range portRange;
|
||||
|
||||
av = *_av;
|
||||
ac = *_ac;
|
||||
space = 0;
|
||||
lsnat = 0;
|
||||
numLocalPorts = 0;
|
||||
|
||||
if (len >= SOF_REDIR) {
|
||||
r = (struct cfg_redir *)spool_buf;
|
||||
/* Skip cfg_redir at beginning of buf. */
|
||||
spool_buf = &spool_buf[SOF_REDIR];
|
||||
space = SOF_REDIR;
|
||||
len -= SOF_REDIR;
|
||||
} else
|
||||
goto nospace;
|
||||
r->mode = REDIR_PORT;
|
||||
/*
|
||||
* Extract protocol.
|
||||
*/
|
||||
if (ac == 0)
|
||||
errx (EX_DATAERR, "redirect_port: missing protocol");
|
||||
r->proto = StrToProto(*av);
|
||||
protoName = *av;
|
||||
INC_ARGCV();
|
||||
|
||||
/*
|
||||
* Extract local address.
|
||||
*/
|
||||
if (ac == 0)
|
||||
errx (EX_DATAERR, "redirect_port: missing local address");
|
||||
|
||||
sep = strchr(*av, ',');
|
||||
/* LSNAT redirection syntax. */
|
||||
if (sep) {
|
||||
r->laddr.s_addr = INADDR_NONE;
|
||||
r->lport = ~0;
|
||||
numLocalPorts = 1;
|
||||
/* Preserve av, copy spool servers to tmp_spool_buf. */
|
||||
strncpy(tmp_spool_buf, *av, strlen(*av)+1);
|
||||
lsnat = 1;
|
||||
} else {
|
||||
if (StrToAddrAndPortRange (*av, &r->laddr, protoName,
|
||||
&portRange) != 0)
|
||||
errx(EX_DATAERR, "redirect_port:"
|
||||
"invalid local port range");
|
||||
|
||||
r->lport = GETLOPORT(portRange);
|
||||
numLocalPorts = GETNUMPORTS(portRange);
|
||||
}
|
||||
INC_ARGCV();
|
||||
|
||||
/*
|
||||
* Extract public port and optionally address.
|
||||
*/
|
||||
if (ac == 0)
|
||||
errx (EX_DATAERR, "redirect_port: missing public port");
|
||||
|
||||
sep = strchr (*av, ':');
|
||||
if (sep) {
|
||||
if (StrToAddrAndPortRange (*av, &r->paddr, protoName,
|
||||
&portRange) != 0)
|
||||
errx(EX_DATAERR, "redirect_port:"
|
||||
"invalid public port range");
|
||||
} else {
|
||||
r->paddr.s_addr = INADDR_ANY;
|
||||
if (StrToPortRange (*av, protoName, &portRange) != 0)
|
||||
errx(EX_DATAERR, "redirect_port:"
|
||||
"invalid public port range");
|
||||
}
|
||||
|
||||
r->pport = GETLOPORT(portRange);
|
||||
r->pport_cnt = GETNUMPORTS(portRange);
|
||||
INC_ARGCV();
|
||||
|
||||
/*
|
||||
* Extract remote address and optionally port.
|
||||
*/
|
||||
/*
|
||||
* NB: isalpha(**av) => we've to check that next parameter is really an
|
||||
* option for this redirect entry, else stop here processing arg[cv].
|
||||
*/
|
||||
if (ac != 0 && !isalpha(**av)) {
|
||||
sep = strchr (*av, ':');
|
||||
if (sep) {
|
||||
if (StrToAddrAndPortRange (*av, &r->raddr, protoName,
|
||||
&portRange) != 0)
|
||||
errx(EX_DATAERR, "redirect_port:"
|
||||
"invalid remote port range");
|
||||
} else {
|
||||
SETLOPORT(portRange, 0);
|
||||
SETNUMPORTS(portRange, 1);
|
||||
StrToAddr (*av, &r->raddr);
|
||||
}
|
||||
INC_ARGCV();
|
||||
} else {
|
||||
SETLOPORT(portRange, 0);
|
||||
SETNUMPORTS(portRange, 1);
|
||||
r->raddr.s_addr = INADDR_ANY;
|
||||
}
|
||||
r->rport = GETLOPORT(portRange);
|
||||
r->rport_cnt = GETNUMPORTS(portRange);
|
||||
|
||||
/*
|
||||
* Make sure port ranges match up, then add the redirect ports.
|
||||
*/
|
||||
if (numLocalPorts != r->pport_cnt)
|
||||
errx(EX_DATAERR, "redirect_port:"
|
||||
"port ranges must be equal in size");
|
||||
|
||||
/* Remote port range is allowed to be '0' which means all ports. */
|
||||
if (r->rport_cnt != numLocalPorts &&
|
||||
(r->rport_cnt != 1 || r->rport != 0))
|
||||
errx(EX_DATAERR, "redirect_port: remote port must"
|
||||
"be 0 or equal to local port range in size");
|
||||
|
||||
/*
|
||||
* Setup LSNAT server pool.
|
||||
*/
|
||||
if (lsnat) {
|
||||
sep = strtok(tmp_spool_buf, ",");
|
||||
while (sep != NULL) {
|
||||
tmp = (struct cfg_spool *)spool_buf;
|
||||
if (len < SOF_SPOOL)
|
||||
goto nospace;
|
||||
len -= SOF_SPOOL;
|
||||
space += SOF_SPOOL;
|
||||
if (StrToAddrAndPortRange(sep, &tmp->addr, protoName,
|
||||
&portRange) != 0)
|
||||
errx(EX_DATAERR, "redirect_port:"
|
||||
"invalid local port range");
|
||||
if (GETNUMPORTS(portRange) != 1)
|
||||
errx(EX_DATAERR, "redirect_port: local port"
|
||||
"must be single in this context");
|
||||
tmp->port = GETLOPORT(portRange);
|
||||
r->spool_cnt++;
|
||||
/* Point to the next possible cfg_spool. */
|
||||
spool_buf = &spool_buf[SOF_SPOOL];
|
||||
sep = strtok(NULL, ",");
|
||||
}
|
||||
}
|
||||
return (space);
|
||||
nospace:
|
||||
errx(EX_DATAERR, "redirect_port: buf is too small\n");
|
||||
}
|
||||
|
||||
static int
|
||||
setup_redir_proto(char *spool_buf, int len,
|
||||
int *_ac, char ***_av)
|
||||
{
|
||||
char **av;
|
||||
int ac, space;
|
||||
struct protoent *protoent;
|
||||
struct cfg_redir *r;
|
||||
|
||||
av = *_av;
|
||||
ac = *_ac;
|
||||
if (len >= SOF_REDIR) {
|
||||
r = (struct cfg_redir *)spool_buf;
|
||||
/* Skip cfg_redir at beginning of buf. */
|
||||
spool_buf = &spool_buf[SOF_REDIR];
|
||||
space = SOF_REDIR;
|
||||
len -= SOF_REDIR;
|
||||
} else
|
||||
goto nospace;
|
||||
r->mode = REDIR_PROTO;
|
||||
/*
|
||||
* Extract protocol.
|
||||
*/
|
||||
if (ac == 0)
|
||||
errx(EX_DATAERR, "redirect_proto: missing protocol");
|
||||
|
||||
protoent = getprotobyname(*av);
|
||||
if (protoent == NULL)
|
||||
errx(EX_DATAERR, "redirect_proto: unknown protocol %s", *av);
|
||||
else
|
||||
r->proto = protoent->p_proto;
|
||||
|
||||
INC_ARGCV();
|
||||
|
||||
/*
|
||||
* Extract local address.
|
||||
*/
|
||||
if (ac == 0)
|
||||
errx(EX_DATAERR, "redirect_proto: missing local address");
|
||||
else
|
||||
StrToAddr(*av, &r->laddr);
|
||||
|
||||
INC_ARGCV();
|
||||
|
||||
/*
|
||||
* Extract optional public address.
|
||||
*/
|
||||
if (ac == 0) {
|
||||
r->paddr.s_addr = INADDR_ANY;
|
||||
r->raddr.s_addr = INADDR_ANY;
|
||||
} else {
|
||||
/* see above in setup_redir_port() */
|
||||
if (!isalpha(**av)) {
|
||||
StrToAddr(*av, &r->paddr);
|
||||
INC_ARGCV();
|
||||
|
||||
/*
|
||||
* Extract optional remote address.
|
||||
*/
|
||||
/* see above in setup_redir_port() */
|
||||
if (ac!=0 && !isalpha(**av)) {
|
||||
StrToAddr(*av, &r->raddr);
|
||||
INC_ARGCV();
|
||||
}
|
||||
}
|
||||
}
|
||||
return (space);
|
||||
nospace:
|
||||
errx(EX_DATAERR, "redirect_proto: buf is too small\n");
|
||||
}
|
||||
|
||||
static void
|
||||
print_nat_config(unsigned char *buf)
|
||||
{
|
||||
struct cfg_nat *n;
|
||||
int i, cnt, flag, off;
|
||||
struct cfg_redir *t;
|
||||
struct cfg_spool *s;
|
||||
struct protoent *p;
|
||||
|
||||
n = (struct cfg_nat *)buf;
|
||||
flag = 1;
|
||||
off = sizeof(*n);
|
||||
printf("ipfw nat %u config", n->id);
|
||||
if (strlen(n->if_name) != 0)
|
||||
printf(" if %s", n->if_name);
|
||||
else if (n->ip.s_addr != 0)
|
||||
printf(" ip %s", inet_ntoa(n->ip));
|
||||
while (n->mode != 0) {
|
||||
if (n->mode & PKT_ALIAS_LOG) {
|
||||
printf(" log");
|
||||
n->mode &= ~PKT_ALIAS_LOG;
|
||||
} else if (n->mode & PKT_ALIAS_DENY_INCOMING) {
|
||||
printf(" deny_in");
|
||||
n->mode &= ~PKT_ALIAS_DENY_INCOMING;
|
||||
} else if (n->mode & PKT_ALIAS_SAME_PORTS) {
|
||||
printf(" same_ports");
|
||||
n->mode &= ~PKT_ALIAS_SAME_PORTS;
|
||||
} else if (n->mode & PKT_ALIAS_UNREGISTERED_ONLY) {
|
||||
printf(" unreg_only");
|
||||
n->mode &= ~PKT_ALIAS_UNREGISTERED_ONLY;
|
||||
} else if (n->mode & PKT_ALIAS_RESET_ON_ADDR_CHANGE) {
|
||||
printf(" reset");
|
||||
n->mode &= ~PKT_ALIAS_RESET_ON_ADDR_CHANGE;
|
||||
} else if (n->mode & PKT_ALIAS_REVERSE) {
|
||||
printf(" reverse");
|
||||
n->mode &= ~PKT_ALIAS_REVERSE;
|
||||
} else if (n->mode & PKT_ALIAS_PROXY_ONLY) {
|
||||
printf(" proxy_only");
|
||||
n->mode &= ~PKT_ALIAS_PROXY_ONLY;
|
||||
}
|
||||
}
|
||||
/* Print all the redirect's data configuration. */
|
||||
for (cnt = 0; cnt < n->redir_cnt; cnt++) {
|
||||
t = (struct cfg_redir *)&buf[off];
|
||||
off += SOF_REDIR;
|
||||
switch (t->mode) {
|
||||
case REDIR_ADDR:
|
||||
printf(" redirect_addr");
|
||||
if (t->spool_cnt == 0)
|
||||
printf(" %s", inet_ntoa(t->laddr));
|
||||
else
|
||||
for (i = 0; i < t->spool_cnt; i++) {
|
||||
s = (struct cfg_spool *)&buf[off];
|
||||
if (i)
|
||||
printf(",");
|
||||
else
|
||||
printf(" ");
|
||||
printf("%s", inet_ntoa(s->addr));
|
||||
off += SOF_SPOOL;
|
||||
}
|
||||
printf(" %s", inet_ntoa(t->paddr));
|
||||
break;
|
||||
case REDIR_PORT:
|
||||
p = getprotobynumber(t->proto);
|
||||
printf(" redirect_port %s ", p->p_name);
|
||||
if (!t->spool_cnt) {
|
||||
printf("%s:%u", inet_ntoa(t->laddr), t->lport);
|
||||
if (t->pport_cnt > 1)
|
||||
printf("-%u", t->lport +
|
||||
t->pport_cnt - 1);
|
||||
} else
|
||||
for (i=0; i < t->spool_cnt; i++) {
|
||||
s = (struct cfg_spool *)&buf[off];
|
||||
if (i)
|
||||
printf(",");
|
||||
printf("%s:%u", inet_ntoa(s->addr),
|
||||
s->port);
|
||||
off += SOF_SPOOL;
|
||||
}
|
||||
|
||||
printf(" ");
|
||||
if (t->paddr.s_addr)
|
||||
printf("%s:", inet_ntoa(t->paddr));
|
||||
printf("%u", t->pport);
|
||||
if (!t->spool_cnt && t->pport_cnt > 1)
|
||||
printf("-%u", t->pport + t->pport_cnt - 1);
|
||||
|
||||
if (t->raddr.s_addr) {
|
||||
printf(" %s", inet_ntoa(t->raddr));
|
||||
if (t->rport) {
|
||||
printf(":%u", t->rport);
|
||||
if (!t->spool_cnt && t->rport_cnt > 1)
|
||||
printf("-%u", t->rport +
|
||||
t->rport_cnt - 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case REDIR_PROTO:
|
||||
p = getprotobynumber(t->proto);
|
||||
printf(" redirect_proto %s %s", p->p_name,
|
||||
inet_ntoa(t->laddr));
|
||||
if (t->paddr.s_addr != 0) {
|
||||
printf(" %s", inet_ntoa(t->paddr));
|
||||
if (t->raddr.s_addr)
|
||||
printf(" %s", inet_ntoa(t->raddr));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
errx(EX_DATAERR, "unknown redir mode");
|
||||
break;
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void
|
||||
ipfw_config_nat(int ac, char **av)
|
||||
{
|
||||
struct cfg_nat *n; /* Nat instance configuration. */
|
||||
int i, len, off, tok;
|
||||
char *id, buf[NAT_BUF_LEN]; /* Buffer for serialized data. */
|
||||
|
||||
len = NAT_BUF_LEN;
|
||||
/* Offset in buf: save space for n at the beginning. */
|
||||
off = sizeof(*n);
|
||||
memset(buf, 0, sizeof(buf));
|
||||
n = (struct cfg_nat *)buf;
|
||||
|
||||
av++; ac--;
|
||||
/* Nat id. */
|
||||
if (ac && isdigit(**av)) {
|
||||
id = *av;
|
||||
i = atoi(*av);
|
||||
ac--; av++;
|
||||
n->id = i;
|
||||
} else
|
||||
errx(EX_DATAERR, "missing nat id");
|
||||
if (ac == 0)
|
||||
errx(EX_DATAERR, "missing option");
|
||||
|
||||
while (ac > 0) {
|
||||
tok = match_token(nat_params, *av);
|
||||
ac--; av++;
|
||||
switch (tok) {
|
||||
case TOK_IP:
|
||||
if (ac == 0)
|
||||
errx(EX_DATAERR, "missing option");
|
||||
if (!inet_aton(av[0], &(n->ip)))
|
||||
errx(EX_DATAERR, "bad ip address ``%s''",
|
||||
av[0]);
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_IF:
|
||||
if (ac == 0)
|
||||
errx(EX_DATAERR, "missing option");
|
||||
set_addr_dynamic(av[0], n);
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_ALOG:
|
||||
n->mode |= PKT_ALIAS_LOG;
|
||||
break;
|
||||
case TOK_DENY_INC:
|
||||
n->mode |= PKT_ALIAS_DENY_INCOMING;
|
||||
break;
|
||||
case TOK_SAME_PORTS:
|
||||
n->mode |= PKT_ALIAS_SAME_PORTS;
|
||||
break;
|
||||
case TOK_UNREG_ONLY:
|
||||
n->mode |= PKT_ALIAS_UNREGISTERED_ONLY;
|
||||
break;
|
||||
case TOK_RESET_ADDR:
|
||||
n->mode |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
|
||||
break;
|
||||
case TOK_ALIAS_REV:
|
||||
n->mode |= PKT_ALIAS_REVERSE;
|
||||
break;
|
||||
case TOK_PROXY_ONLY:
|
||||
n->mode |= PKT_ALIAS_PROXY_ONLY;
|
||||
break;
|
||||
/*
|
||||
* All the setup_redir_* functions work directly in the final
|
||||
* buffer, see above for details.
|
||||
*/
|
||||
case TOK_REDIR_ADDR:
|
||||
case TOK_REDIR_PORT:
|
||||
case TOK_REDIR_PROTO:
|
||||
switch (tok) {
|
||||
case TOK_REDIR_ADDR:
|
||||
i = setup_redir_addr(&buf[off], len, &ac, &av);
|
||||
break;
|
||||
case TOK_REDIR_PORT:
|
||||
i = setup_redir_port(&buf[off], len, &ac, &av);
|
||||
break;
|
||||
case TOK_REDIR_PROTO:
|
||||
i = setup_redir_proto(&buf[off], len, &ac, &av);
|
||||
break;
|
||||
}
|
||||
n->redir_cnt++;
|
||||
off += i;
|
||||
len -= i;
|
||||
break;
|
||||
default:
|
||||
errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]);
|
||||
}
|
||||
}
|
||||
|
||||
i = do_cmd(IP_FW_NAT_CFG, buf, off);
|
||||
if (i)
|
||||
err(1, "setsockopt(%s)", "IP_FW_NAT_CFG");
|
||||
|
||||
if (!co.do_quiet) {
|
||||
/* After every modification, we show the resultant rule. */
|
||||
int _ac = 3;
|
||||
char *_av[] = {"show", "config", id};
|
||||
ipfw_show_nat(_ac, _av);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ipfw_show_nat(int ac, char **av)
|
||||
{
|
||||
struct cfg_nat *n;
|
||||
struct cfg_redir *e;
|
||||
int cmd, i, nbytes, do_cfg, do_rule, frule, lrule, nalloc, size;
|
||||
int nat_cnt, redir_cnt, r;
|
||||
uint8_t *data, *p;
|
||||
char *endptr;
|
||||
|
||||
do_rule = 0;
|
||||
nalloc = 1024;
|
||||
size = 0;
|
||||
data = NULL;
|
||||
frule = 0;
|
||||
lrule = IPFW_DEFAULT_RULE; /* max ipfw rule number */
|
||||
ac--; av++;
|
||||
|
||||
if (co.test_only)
|
||||
return;
|
||||
|
||||
/* Parse parameters. */
|
||||
for (cmd = IP_FW_NAT_GET_LOG, do_cfg = 0; ac != 0; ac--, av++) {
|
||||
if (!strncmp(av[0], "config", strlen(av[0]))) {
|
||||
cmd = IP_FW_NAT_GET_CONFIG, do_cfg = 1;
|
||||
continue;
|
||||
}
|
||||
/* Convert command line rule #. */
|
||||
frule = lrule = strtoul(av[0], &endptr, 10);
|
||||
if (*endptr == '-')
|
||||
lrule = strtoul(endptr+1, &endptr, 10);
|
||||
if (lrule == 0)
|
||||
err(EX_USAGE, "invalid rule number: %s", av[0]);
|
||||
do_rule = 1;
|
||||
}
|
||||
|
||||
nbytes = nalloc;
|
||||
while (nbytes >= nalloc) {
|
||||
nalloc = nalloc * 2;
|
||||
nbytes = nalloc;
|
||||
data = safe_realloc(data, nbytes);
|
||||
if (do_cmd(cmd, data, (uintptr_t)&nbytes) < 0)
|
||||
err(EX_OSERR, "getsockopt(IP_FW_GET_%s)",
|
||||
(cmd == IP_FW_NAT_GET_LOG) ? "LOG" : "CONFIG");
|
||||
}
|
||||
if (nbytes == 0)
|
||||
exit(0);
|
||||
if (do_cfg) {
|
||||
nat_cnt = *((int *)data);
|
||||
for (i = sizeof(nat_cnt); nat_cnt; nat_cnt--) {
|
||||
n = (struct cfg_nat *)&data[i];
|
||||
if (frule <= n->id && lrule >= n->id)
|
||||
print_nat_config(&data[i]);
|
||||
i += sizeof(struct cfg_nat);
|
||||
for (redir_cnt = 0; redir_cnt < n->redir_cnt; redir_cnt++) {
|
||||
e = (struct cfg_redir *)&data[i];
|
||||
i += sizeof(struct cfg_redir) + e->spool_cnt *
|
||||
sizeof(struct cfg_spool);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i = 0; 1; i += LIBALIAS_BUF_SIZE + sizeof(int)) {
|
||||
p = &data[i];
|
||||
if (p == data + nbytes)
|
||||
break;
|
||||
bcopy(p, &r, sizeof(int));
|
||||
if (do_rule) {
|
||||
if (!(frule <= r && lrule >= r))
|
||||
continue;
|
||||
}
|
||||
printf("nat %u: %s\n", r, p+sizeof(int));
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user