diff --git a/include/linux/if_map.h b/include/linux/if_map.h index e69de29..b1569d2 100644 --- a/include/linux/if_map.h +++ b/include/linux/if_map.h @@ -0,0 +1,121 @@ +/* + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * wrote this file. As long as you retain this notice + * you can do whatever you want with this stuff. If we meet some day, and you + * think this stuff is worth it, you can buy me a beer in return Masakazu Asama + * ---------------------------------------------------------------------------- + */ + +#ifndef _IF_MAP_H_ +#define _IF_MAP_H_ + +#include +#include + +#define SIOCGETMAP (SIOCDEVPRIVATE + 0) +#define SIOCADDMAP (SIOCDEVPRIVATE + 1) +#define SIOCDELMAP (SIOCDEVPRIVATE + 2) +#define SIOCCHGMAP (SIOCDEVPRIVATE + 3) + +#define SIOCGETMAPRULES (SIOCDEVPRIVATE + 4) +#define SIOCADDMAPRULES (SIOCDEVPRIVATE + 5) +#define SIOCDELMAPRULES (SIOCDEVPRIVATE + 6) +#define SIOCCHGMAPRULES (SIOCDEVPRIVATE + 7) + +#define SIOCGETMAPCURRNUM (SIOCDEVPRIVATE + 8) +#define SIOCGETMAPCURR (SIOCDEVPRIVATE + 9) +#define SIOCGETMAPNAPTNUM (SIOCDEVPRIVATE + 10) +#define SIOCGETMAPNAPT (SIOCDEVPRIVATE + 11) + +#define SIOCGETMAPPOOLS (SIOCDEVPRIVATE + 12) +#define SIOCADDMAPPOOLS (SIOCDEVPRIVATE + 13) +#define SIOCDELMAPPOOLS (SIOCDEVPRIVATE + 14) +#define SIOCCHGMAPPOOLS (SIOCDEVPRIVATE + 15) + +#define MAP_ROLE_BR (1 << 0) +#define MAP_ROLE_CE (1 << 1) + +#define MAP_FORWARDING_MODE_T (1 << 0) +#define MAP_FORWARDING_MODE_E (1 << 1) + +#define MAP_FORWARDING_RULE_T (1 << 0) +#define MAP_FORWARDING_RULE_F (1 << 1) + +#define MAP_NAPT_ALWAYS_T (1 << 0) +#define MAP_NAPT_ALWAYS_F (1 << 1) + +#define MAP_NAPT_FORCE_RECYCLE_T (1 << 0) +#define MAP_NAPT_FORCE_RECYCLE_F (1 << 1) + +#define MAP_IPV4_FRAG_INNER_T (1 << 0) +#define MAP_IPV4_FRAG_INNER_F (1 << 1) + +struct map_rule_parm { + struct in6_addr ipv6_prefix; + __u8 ipv6_prefix_length; + __be32 ipv4_prefix; + __u8 ipv4_prefix_length; + __u16 psid_prefix; + __u8 psid_prefix_length; + __u8 ea_length; + __u8 psid_offset; + __u8 forwarding_mode; + __u8 forwarding_rule; +}; + +struct map_pool_parm { + __be32 pool_prefix; + __u8 pool_prefix_length; +}; + +struct map_parm { + char name[IFNAMSIZ]; + int tunnel_source; + struct in6_addr br_address; + __u8 br_address_length; + __u8 role; + __u8 default_forwarding_mode; + __u8 default_forwarding_rule; + int ipv6_fragment_size; + __u8 ipv4_fragment_inner; + __u8 napt_always; + __u8 napt_force_recycle; + unsigned long rule_num; + unsigned long pool_num; + struct map_rule_parm rule[0]; + struct map_pool_parm pool[0]; +}; + +struct map_napt_node_parm { + __be32 raddr, laddr, maddr; + __be16 rport, lport, mport; + struct in6_addr laddr6; + __u8 proto; + __u8 flags; + struct timespec last_used; +}; + +struct map_napt_parm { + struct timespec current_time; + unsigned long napt_node_num; + struct map_napt_node_parm napt_node[0]; +}; + +struct map_napt_block { + __u16 min, max; +}; + +struct map_current_parm { + int has_bmr; + struct map_rule_parm bmrp; + struct in6_addr map_ipv6_address; + __u8 map_ipv6_address_length; + __be32 laddr4; + __u16 psid; + int psid_length; + int port_range_length; + struct map_napt_block port_range[0]; +}; + +#endif /* _IF_MAP_H_ */ diff --git a/ip/Makefile b/ip/Makefile index 6a518f8..45f9a8e 100644 --- a/ip/Makefile +++ b/ip/Makefile @@ -3,7 +3,7 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.o \ ipmaddr.o ipmonitor.o ipmroute.o ipprefix.o iptuntap.o \ ipxfrm.o xfrm_state.o xfrm_policy.o xfrm_monitor.o \ iplink_vlan.o link_veth.o link_gre.o iplink_can.o \ - iplink_macvlan.o iplink_macvtap.o ipl2tp.o link_vti.o + iplink_macvlan.o iplink_macvtap.o ipl2tp.o link_vti.o ipmap.o RTMONOBJ=rtmon.o diff --git a/ip/ip.c b/ip/ip.c index 4e8ac5c..f5b58d5 100644 --- a/ip/ip.c +++ b/ip/ip.c @@ -45,7 +45,7 @@ static void usage(void) " ip [ -force ] -batch filename\n" "where OBJECT := { link | addr | addrlabel | route | rule | neigh | ntable |\n" " tunnel | tuntap | maddr | mroute | mrule | monitor | xfrm |\n" -" netns | l2tp }\n" +" netns | l2tp | map }\n" " OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n" " -f[amily] { inet | inet6 | ipx | dnet | link } |\n" " -l[oops] { maximum-addr-flush-attempts } |\n" @@ -83,6 +83,7 @@ static const struct cmd { { "mroute", do_multiroute }, { "mrule", do_multirule }, { "netns", do_netns }, + { "map", do_ipmap }, { "help", do_help }, { 0 } }; diff --git a/ip/ip_common.h b/ip/ip_common.h index 5fa2cc0..01dbcc8 100644 --- a/ip/ip_common.h +++ b/ip/ip_common.h @@ -42,6 +42,7 @@ extern int do_multirule(int argc, char **argv); extern int do_netns(int argc, char **argv); extern int do_xfrm(int argc, char **argv); extern int do_ipl2tp(int argc, char **argv); +extern int do_ipmap(int argc, char **argv); static inline int rtm_get_table(struct rtmsg *r, struct rtattr **tb) { diff --git a/ip/ipmap.c b/ip/ipmap.c index e69de29..534c528 100644 --- a/ip/ipmap.c +++ b/ip/ipmap.c @@ -0,0 +1,1042 @@ +/* + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * wrote this file. As long as you retain this notice + * you can do whatever you want with this stuff. If we meet some day, and you + * think this stuff is worth it, you can buy me a beer in return Masakazu Asama + * ---------------------------------------------------------------------------- + */ +/* + * ipmap.c "ip map" + * + * Authors: Masakazu Asama, + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "ip_common.h" + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip map { add | change | del } [ NAME ]\n"); + fprintf(stderr, " [ role { br | ce } ] [ tunnel-source STRING ] [ br-address ADDR ]\n"); + fprintf(stderr, " [ default-forwarding-mode { translation | encapsulation } ]\n"); + fprintf(stderr, " [ default-forwarding-rule BOOL ]\n"); + fprintf(stderr, " [ napt-always BOOL ] [ napt-force-recycle BOOL ]\n"); + fprintf(stderr, " [ ipv6-fragment-size SIZE ] [ ipv4-fragment-inner BOOL ]\n"); + fprintf(stderr, " [ dev PHYS_DEV ]\n"); + fprintf(stderr, "Usage: ip map { add-rule | del-rule } [ NAME ]\n"); + fprintf(stderr, " [ ipv6-prefix ADDR ] [ ipv4-prefix ADDR ] [ psid-prefix NUM/LEN]\n"); + fprintf(stderr, " [ ea-length NUMBER ]\n"); + fprintf(stderr, " [ forwarding-mode { translation | encapsulation } ]\n"); + fprintf(stderr, " [ forwarding-rule BOOL ] [ psid-offset NUMBER ] [ dev PHYS_DEV ]\n"); + fprintf(stderr, "Usage: ip map { add-pool | del-pool } [ NAME ]\n"); + fprintf(stderr, " [ pool-prefix ADDR ] [ dev PHYS_DEV ]\n"); + fprintf(stderr, "Usage: ip map { show | show-parm | show-rule | show-napt } [ NAME ]\n"); + fprintf(stderr, " [ dev PHYS_DEV ]\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Where: NAME := STRING\n"); + fprintf(stderr, " ADDR := IP_ADDRESS\n"); + fprintf(stderr, " BOOL := { true | false }\n"); + exit(-1); +} + +static inline int ipv6_addr_any(const struct in6_addr *a) +{ + return (a->s6_addr32[0] | a->s6_addr32[1] | a->s6_addr32[2] | a->s6_addr32[3]) == 0; +} + +static inline void ipv6_addr_prefix(struct in6_addr *pfx, const struct in6_addr *addr, int plen) +{ + int o = plen >> 3, b = plen & 0x7; + memset(pfx->s6_addr, 0, sizeof(pfx->s6_addr)); + memcpy(pfx->s6_addr, addr, o); + if (b != 0) + pfx->s6_addr[o] = addr->s6_addr[o] & (0xff00 >> b); +} + +static inline int ipv6_addr_equal(const struct in6_addr *a1, const struct in6_addr *a2) +{ + return ((a1->s6_addr32[0] ^ a2->s6_addr32[0]) | + (a1->s6_addr32[1] ^ a2->s6_addr32[1]) | + (a1->s6_addr32[2] ^ a2->s6_addr32[2]) | + (a1->s6_addr32[3] ^ a2->s6_addr32[3])) == 0; +} + +static void debug_dump_map_parm(struct map_parm *p) +{ + char s1[1024]; + fprintf(stderr, "debug_dump_map_parm:\n"); + fprintf(stderr, " name = \"%s\"\n", p->name); + fprintf(stderr, " tunnel_source = %d\n", p->tunnel_source); + fprintf(stderr, " br_address = %s\n", inet_ntop(AF_INET6, &p->br_address, s1, sizeof(s1))); + fprintf(stderr, " br_address_length = %d\n", p->br_address_length); + fprintf(stderr, " role = %02x\n", p->role); + fprintf(stderr, " default_forwarding_mode = %02x\n", p->default_forwarding_mode); + fprintf(stderr, " default_forwarding_rule = %02x\n", p->default_forwarding_rule); + fprintf(stderr, " ipv6_fragment_size = %d\n", p->ipv6_fragment_size); + fprintf(stderr, " ipv4_fragment_inner = %s\n", + (p->ipv4_fragment_inner == MAP_IPV4_FRAG_INNER_T ? "true" : "false")); +} + +static void debug_dump_map_rule_parm(struct map_rule_parm *r) +{ + char s1[1024]; + char s2[1024]; + fprintf(stderr, "debug_dump_map_rule_parm:\n"); + fprintf(stderr, " ipv6_prefix = %s\n", inet_ntop(AF_INET6, &r->ipv6_prefix, s1, sizeof(s1))); + fprintf(stderr, " ipv6_prefix_length = %d\n", r->ipv6_prefix_length); + fprintf(stderr, " ipv4_prefix = %s\n", format_host(AF_INET, 4, &r->ipv4_prefix, s2, sizeof(s2))); + fprintf(stderr, " ipv4_prefix_length = %d\n", r->ipv4_prefix_length); + fprintf(stderr, " psid_prefix = 0x%04x\n", r->psid_prefix); + fprintf(stderr, " psid_prefix_length = %d\n", r->psid_prefix_length); + fprintf(stderr, " ea_length = %d\n", r->ea_length); + fprintf(stderr, " psid_offset = %d\n", r->psid_offset); + fprintf(stderr, " forwarding_mode = %02x\n", r->forwarding_mode); + fprintf(stderr, " forwarding_rule = %02x\n", r->forwarding_rule); +} + +static int map_ioctl(const char *name, int cmd, void *p) +{ + struct ifreq ifr; + int fd; + int err; + + if (cmd == SIOCADDMAP || cmd == SIOCDELMAP) + strncpy(ifr.ifr_name, "mapfb", IFNAMSIZ - 1); + else + strncpy(ifr.ifr_name, name, IFNAMSIZ - 1); + ifr.ifr_ifru.ifru_data = p; + fd = socket(AF_INET6, SOCK_DGRAM, 0); + err = ioctl(fd, cmd, &ifr); + if (err) + fprintf(stderr, "%s ioctl failed: %s\n", + ((cmd == SIOCADDMAP || cmd == SIOCDELMAP) ? "mapfb" : name), + strerror(errno)); + close(fd); + return err; +} + +static int parse_args(int argc, char **argv, int cmd, struct map_parm *p, struct map_rule_parm *rp, struct map_pool_parm *pp) +{ + int count = 0; + inet_prefix prefix; + + memset(p, 0, sizeof(*p)); + memset(rp, 0, sizeof(*rp)); + memset(pp, 0, sizeof(*pp)); + + /* default settings */ + p->role = MAP_ROLE_CE; + p->default_forwarding_mode = MAP_FORWARDING_MODE_E; + p->default_forwarding_rule = MAP_FORWARDING_RULE_T; + p->ipv6_fragment_size = 1280; + p->ipv4_fragment_inner = MAP_IPV4_FRAG_INNER_T; + p->napt_always = MAP_NAPT_ALWAYS_T; + p->napt_force_recycle = MAP_NAPT_FORCE_RECYCLE_F; + rp->psid_prefix = 0; + rp->psid_prefix_length = 0; + rp->psid_offset = 6; + pp->pool_prefix = 0; + pp->pool_prefix_length = 0; + + while (argc > 0) { + if (strcmp(*argv, "role") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "br") == 0) { + p->role = MAP_ROLE_BR; + } else if (strcmp(*argv, "ce") == 0) { + p->role = MAP_ROLE_CE; + } else { + fprintf(stderr, "Cannot guess MAP role.\n"); + exit(-1); + } + } else if (strcmp(*argv, "tunnel-source") == 0) { + NEXT_ARG(); + p->tunnel_source = if_nametoindex(*argv); + if (p->tunnel_source == 0) { + fprintf(stderr, "No such interface: %s.\n", *argv); + exit(-1); + } + } else if (strcmp(*argv, "br-address") == 0) { + NEXT_ARG(); + if (get_prefix(&prefix, *argv, AF_INET6)) { + fprintf(stderr, "Invalid br-address: %s.\n", *argv); + exit(-1); + } + memcpy(&p->br_address, prefix.data, 16); + p->br_address_length = prefix.bitlen; + } else if (strcmp(*argv, "default-forwarding-mode") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "translation") == 0) { + p->default_forwarding_mode = MAP_FORWARDING_MODE_T; + } else if (strcmp(*argv, "encapsulation") == 0) { + p->default_forwarding_mode = MAP_FORWARDING_MODE_E; + } else { + fprintf(stderr, "Cannot guess default forwarding mode.\n"); + exit(-1); + } + } else if (strcmp(*argv, "default-forwarding-rule") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "true") == 0) { + p->default_forwarding_rule = MAP_FORWARDING_RULE_T; + } else if (strcmp(*argv, "false") == 0) { + p->default_forwarding_rule = MAP_FORWARDING_RULE_F; + } else { + fprintf(stderr, "Cannot guess default forwardng rule.\n"); + exit(-1); + } + } else if (strcmp(*argv, "napt-always") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "true") == 0) { + p->napt_always = MAP_NAPT_ALWAYS_T; + } else if (strcmp(*argv, "false") == 0) { + p->napt_always = MAP_NAPT_ALWAYS_F; + } else { + fprintf(stderr, "Cannot guess napt-always.\n"); + exit(-1); + } + } else if (strcmp(*argv, "napt-force-recycle") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "true") == 0) { + p->napt_force_recycle = MAP_NAPT_FORCE_RECYCLE_T; + } else if (strcmp(*argv, "false") == 0) { + p->napt_force_recycle = MAP_NAPT_FORCE_RECYCLE_F; + } else { + fprintf(stderr, "Cannot guess napt-force-recycle.\n"); + exit(-1); + } + } else if (strcmp(*argv, "ipv6-fragment-size") == 0) { + unsigned uval; + NEXT_ARG(); + if (get_unsigned(&uval, *argv, 0)) { + fprintf(stderr, "Invalid ipv6-fragment-size: %s.\n", *argv); + exit(-1); + } + if (uval < 1280) { + fprintf(stderr, "Invalid ipv6-fragment-size: %s.\n", *argv); + exit(-1); + } + p->ipv6_fragment_size = uval; + } else if (strcmp(*argv, "ipv4-fragment-inner") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "true") == 0) { + p->ipv4_fragment_inner = MAP_IPV4_FRAG_INNER_T; + } else if (strcmp(*argv, "false") == 0) { + p->ipv4_fragment_inner = MAP_IPV4_FRAG_INNER_F; + } else { + fprintf(stderr, "Cannot guess ipv4-fragment-inner.\n"); + exit(-1); + } + } else if (strcmp(*argv, "ipv6-prefix") == 0) { + NEXT_ARG(); + if (get_prefix(&prefix, *argv, AF_INET6)) { + fprintf(stderr, "Invalid ipv6-prefix: %s.\n", *argv); + exit(-1); + } + memcpy(&rp->ipv6_prefix, prefix.data, 16); + rp->ipv6_prefix_length = prefix.bitlen; + } else if (strcmp(*argv, "ipv4-prefix") == 0) { + NEXT_ARG(); + if (get_prefix(&prefix, *argv, AF_INET)) { + fprintf(stderr, "Invalid ipv4-prefix: %s.\n", *argv); + exit(-1); + } + memcpy(&rp->ipv4_prefix, prefix.data, 4); + rp->ipv4_prefix_length = prefix.bitlen; + } else if (strcmp(*argv, "psid-prefix") == 0) { + __u16 psid_prefix; + unsigned psid_prefix_length; + char *lenpos; + NEXT_ARG(); + lenpos = strchr(*argv, '/'); + if (!lenpos) { + fprintf(stderr, "Invalid psid-prefix: %s.\n", *argv); + exit(-1); + } + *lenpos = '\0'; + ++lenpos; + if (memcmp("0x", *argv, 2) == 0) { + if (get_u16(&psid_prefix, *argv, 16)) { + fprintf(stderr, "Invalid psid-prefix: %s.\n", *argv); + exit(-1); + } + } else { + if (get_u16(&psid_prefix, *argv, 10)) { + fprintf(stderr, "Invalid psid-prefix: %s.\n", *argv); + exit(-1); + } + } + rp->psid_prefix = psid_prefix; + if (get_unsigned(&psid_prefix_length, lenpos, 0)) { + fprintf(stderr, "Invalid psid-prefix: %s.\n", lenpos); + exit(-1); + } + if (psid_prefix_length > 16) { + fprintf(stderr, "Invalid psid-prefix: %s.\n", lenpos); + exit(-1); + } + rp->psid_prefix_length = psid_prefix_length; + } else if (strcmp(*argv, "ea-length") == 0) { + unsigned uval; + NEXT_ARG(); + if (get_unsigned(&uval, *argv, 0)) { + fprintf(stderr, "Invalid ea-length: %s.\n", *argv); + exit(-1); + } + if (uval > 48) { + fprintf(stderr, "Invalid ea-length: %s.\n", *argv); + exit(-1); + } + rp->ea_length = uval; + } else if (strcmp(*argv, "forwarding-mode") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "translation") == 0) { + rp->forwarding_mode = MAP_FORWARDING_MODE_T; + } else if (strcmp(*argv, "encapsulation") == 0) { + rp->forwarding_mode = MAP_FORWARDING_MODE_E; + } else { + fprintf(stderr, "Cannot guess forwarding mode.\n"); + exit(-1); + } + } else if (strcmp(*argv, "forwarding-rule") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "true") == 0) { + rp->forwarding_rule = MAP_FORWARDING_RULE_T; + } else if (strcmp(*argv, "false") == 0) { + rp->forwarding_rule = MAP_FORWARDING_RULE_F; + } else { + fprintf(stderr, "Cannot guess forwarding rule.\n"); + exit(-1); + } + } else if (strcmp(*argv, "psid-offset") == 0) { + unsigned uval; + NEXT_ARG(); + if (get_unsigned(&uval, *argv, 0)) { + fprintf(stderr, "Invalid psid-offset: %s.\n", *argv); + exit(-1); + } + if (uval > 16) { + fprintf(stderr, "Invalid psid-offset: %s.\n", *argv); + exit(-1); + } + rp->psid_offset = uval; + } else if (strcmp(*argv, "pool-prefix") == 0) { + NEXT_ARG(); + if (get_prefix(&prefix, *argv, AF_INET)) { + fprintf(stderr, "Invalid pool-prefix: %s.\n", *argv); + exit(-1); + } + memcpy(&pp->pool_prefix, prefix.data, 4); + pp->pool_prefix_length = prefix.bitlen; + } else if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + strncpy(p->name, *argv, IFNAMSIZ - 1); + } else { + if (strcmp(*argv, "name") == 0) { + NEXT_ARG(); + } else if (matches(*argv, "help") == 0) + usage(); + if (p->name[0]) { + fprintf(stderr, "Duplicated name:%s.\n", *argv); + exit(-1); + } + strncpy(p->name, *argv, IFNAMSIZ - 1); + if (cmd == SIOCCHGMAP && count == 0) { + struct map_parm old_p; + memset(&old_p, 0, sizeof(old_p)); + if (map_ioctl(*argv, SIOCGETMAP, &old_p)) + return -1; + *p = old_p; + } + } + count++; + argc--; argv++; + } + + if (!p->name[0]) { + fprintf(stderr, "name or dev required.\n"); + exit(-1); + } + + if (cmd == SIOCADDMAP || cmd == SIOCCHGMAP) { + if (p->role == MAP_ROLE_CE && p->tunnel_source == 0) { + fprintf(stderr, "tunnel-source required if role equals ce.\n"); + exit(-1); + } + if (ipv6_addr_any(&p->br_address)) { + fprintf(stderr, "br-address required.\n"); + exit(-1); + } + /* + if (p->role == MAP_ROLE_BR && p->br_address_length > 64) { + fprintf(stderr, "br-address prefix length invalid.\n"); + exit(-1); + } + */ + if (p->br_address_length > 96) { + fprintf(stderr, "br-address prefix length invalid.\n"); + exit(-1); + } + if (!p->default_forwarding_mode) { + fprintf(stderr, "default-forwarding-mode required.\n"); + exit(-1); + } + } + + if (cmd == SIOCADDMAPRULES || cmd == SIOCCHGMAPRULES) { + struct in6_addr t; + __u32 amask; + __u16 pmask; + /* + * ipv6-prefix validation. + */ + if (ipv6_addr_any(&rp->ipv6_prefix)) { + fprintf(stderr, "ipv6-prefix required or invalid.\n"); + exit(-1); + } + ipv6_addr_prefix(&t, &rp->ipv6_prefix, rp->ipv6_prefix_length); + if (!ipv6_addr_equal(&t, &rp->ipv6_prefix)) { + fprintf(stderr, "ipv6-prefix invalid.\n"); + exit(-1); + } + /* + * ipv4-prefix validation. + */ + if (!rp->ipv4_prefix || !rp->ipv4_prefix_length) { + fprintf(stderr, "ipv4-prefix required or invalid.\n"); + exit(-1); + } + amask = 0xffffffff; + if (rp->ipv4_prefix_length < 32) + amask = ~((1 << (32 - rp->ipv4_prefix_length)) - 1); + if ((ntohl(rp->ipv4_prefix) & amask) != ntohl(rp->ipv4_prefix)) { + fprintf(stderr, "ipv4-prefix invalid.\n"); + exit(-1); + } + /* + * psid-prefix validation. + */ + pmask = 0xffff; + if (rp->psid_prefix_length < 16) + pmask = (1 << rp->psid_prefix_length) - 1; + if ((rp->psid_prefix & pmask) != rp->psid_prefix) { + fprintf(stderr, "psid-prefix invalid.\n"); + exit(-1); + } + /* + * ipv4-prefix and psid-prefix validation. + */ + if (rp->ipv4_prefix_length != 32 && rp->psid_prefix_length > 0) { + fprintf(stderr, "psid-prefix length > 0 but ipv4-prefix length != 32.\n"); + exit(-1); + } + /* + * other validations. + */ + if (rp->ea_length > 48) { + fprintf(stderr, "ea-length required or invalid.\n"); + exit(-1); + } + if (rp->psid_offset > 16) { + fprintf(stderr, "psid-offset invalid.\n"); + exit(-1); + } + if (rp->psid_offset + rp->psid_prefix_length > 16) { + fprintf(stderr, "psid_offset or psid_prefix_length invalid.\n"); + exit(-1); + } + if (rp->ipv6_prefix_length + rp->ea_length > 64) { + fprintf(stderr, "ipv6-prefix length or ea-length invalid.\n"); + exit(-1); + } + if (rp->ipv4_prefix_length + rp->psid_prefix_length + rp->ea_length + rp->psid_offset > 48) { + fprintf(stderr, "ipv4-prefix length or psid-prefix length or ea-length or psid-offset invalid.\n"); + exit(-1); + } + } + + if (cmd == SIOCDELMAPRULES) { + if (ipv6_addr_any(&rp->ipv6_prefix)) { + fprintf(stderr, "ipv6-prefix required or invalid.\n"); + exit(-1); + } + if (!rp->ipv4_prefix) { + fprintf(stderr, "ipv4-prefix required or invalid.\n"); + exit(-1); + } + } + + if (cmd == SIOCADDMAPPOOLS || cmd == SIOCCHGMAPPOOLS || cmd == SIOCDELMAPPOOLS) { + if (!pp->pool_prefix) { + fprintf(stderr, "pool-prefix required or invalid.\n"); + exit(-1); + } + } + + /* XXX: */ + //debug_dump_map_parm(p); + //debug_dump_map_rule_parm(r); + + return 0; +} + +static int do_add(int argc, char **argv) +{ + struct map_parm p; + struct map_rule_parm rp; + struct map_pool_parm pp; + + if (parse_args(argc, argv, SIOCADDMAP, &p, &rp, &pp) < 0) + return -1; + + if (map_ioctl(p.name, SIOCADDMAP, &p)) + return -1; + + return 0; +} + +static int do_change(int argc, char **argv) +{ + struct map_parm p; + struct map_rule_parm rp; + struct map_pool_parm pp; + + if (parse_args(argc, argv, SIOCCHGMAP, &p, &rp, &pp) < 0) + return -1; + + if (map_ioctl(p.name, SIOCCHGMAP, &p)) + return -1; + + return 0; +} + +static int do_del(int argc, char **argv) +{ + struct map_parm p; + struct map_rule_parm rp; + struct map_pool_parm pp; + + if (parse_args(argc, argv, SIOCDELMAP, &p, &rp, &pp) < 0) + return -1; + + if (map_ioctl(p.name, SIOCDELMAP, &p)) + return -1; + + return 0; +} + +static int do_show(int argc, char **argv) +{ + struct map_parm p; + struct map_rule_parm rp; + struct map_pool_parm pp; + struct map_parm *pptr; + struct map_rule_parm *rptr; + char s1[1024]; + char s2[1024]; + int i; + + if (parse_args(argc, argv, SIOCGETMAP, &p, &rp, &pp) < 0) + return -1; + + if (map_ioctl(p.name, SIOCGETMAP, &p)) + return -1; + memset(s1, 0, 1024); + if_indextoname(p.tunnel_source, s1); + if (!s1[0]) + sprintf(s1, "(null)"); + printf("%s: role %s tunnel-source %s br-address %s/%d default-forwarding-mode %s default-forwarding-rule %s ipv6-fragment-size %d ipv4-fragment-inner %s napt-always %s napt-force-recycle %s\n", + p.name, + (p.role == MAP_ROLE_BR ? "br" : "ce"), + s1, + inet_ntop(AF_INET6, &p.br_address, s2, sizeof(s2)), + p.br_address_length, + (p.default_forwarding_mode == MAP_FORWARDING_MODE_T ? "translation" : "encapsulation"), + (p.default_forwarding_rule == MAP_FORWARDING_RULE_T ? "true" : "false"), + p.ipv6_fragment_size, + (p.ipv4_fragment_inner == MAP_IPV4_FRAG_INNER_T ? "true" : "false"), + (p.napt_always == MAP_NAPT_ALWAYS_T ? "true" : "false"), + (p.napt_force_recycle == MAP_NAPT_FORCE_RECYCLE_T ? "true" : "false")); + + pptr = malloc(sizeof(struct map_parm) + sizeof(struct map_rule_parm) * p.rule_num); + if (!pptr) + return -1; + if (map_ioctl(p.name, SIOCGETMAPRULES, pptr)) + return -1; + for (i = 0; i < pptr->rule_num; i++) { + rptr = &pptr->rule[i]; + printf(" %d: ipv6-prefix %s/%d ipv4-prefix %s/%d psid-prefix 0x%x/%d ea-length %d psid-offset %d " + "forwarding-mode %s forwarding-rule %s\n", + i, + inet_ntop(AF_INET6, &rptr->ipv6_prefix, s1, sizeof(s1)), + rptr->ipv6_prefix_length, + format_host(AF_INET, 4, &rptr->ipv4_prefix, s2, sizeof(s2)), + rptr->ipv4_prefix_length, + rptr->psid_prefix, + rptr->psid_prefix_length, + rptr->ea_length, + rptr->psid_offset, + (rptr->forwarding_mode == MAP_FORWARDING_MODE_T ? "translation" : "encapsulation"), + (rptr->forwarding_rule == MAP_FORWARDING_RULE_T ? "true" : "false")); + } + free(pptr); + + return 0; +} + +static int do_show_pool(int argc, char **argv) +{ + struct map_parm p; + struct map_rule_parm rp; + struct map_pool_parm pp; + struct map_parm *pptr; + struct map_pool_parm *ppptr; + char s1[1024]; + int i; + + if (parse_args(argc, argv, SIOCGETMAP, &p, &rp, &pp) < 0) + return -1; + + if (map_ioctl(p.name, SIOCGETMAP, &p)) + return -1; + + pptr = malloc(sizeof(struct map_parm) + sizeof(struct map_pool_parm) * p.pool_num); + if (!pptr) + return -1; + if (map_ioctl(p.name, SIOCGETMAPPOOLS, pptr)) + return -1; + + printf(" IPv4 pool :\n"); + for (i = 0; i < pptr->pool_num; i++) { + ppptr = &pptr->pool[i]; + memset(s1, 0, 1024); + printf(" IPv4 pool #%04d : %s/%d\n", i, + format_host(AF_INET, 4, &ppptr->pool_prefix, s1, sizeof(s1)), + ppptr->pool_prefix_length); + } + + free(pptr); + + return 0; +} + +static int do_show_parm(int argc, char **argv) +{ + struct map_parm p; + struct map_rule_parm rp; + struct map_pool_parm pp; + struct map_current_parm cp, *cpp; + char s1[1024]; + char s2[1024]; + int i, num; + + if (parse_args(argc, argv, SIOCGETMAP, &p, &rp, &pp) < 0) + return -1; + + if (map_ioctl(p.name, SIOCGETMAP, &p)) + return -1; + + memset(s1, 0, 1024); + if_indextoname(p.tunnel_source, s1); + if (!s1[0]) + sprintf(s1, "(null)"); + + if (map_ioctl(p.name, SIOCGETMAPCURRNUM, &cp)) + return -1; + cpp = malloc(sizeof(struct map_current_parm) + sizeof(struct map_napt_block) * cp.port_range_length); + if (!cpp) + return -1; + cpp->port_range_length = cp.port_range_length; + if (map_ioctl(p.name, SIOCGETMAPCURR, cpp)) { + free(cpp); + return -1; + } + + printf("\n"); + printf(" Interface name : %s\n", p.name); + printf(" Role : %s\n", (p.role == MAP_ROLE_BR ? "BR" : "CE")); + printf(" Tunnel source : %s\n", s1); + printf(" BR address : %s/%d\n", + inet_ntop(AF_INET6, &p.br_address, s2, sizeof(s2)), p.br_address_length); + do_show_pool(argc, argv); + printf(" Default forwarding mode : %s\n", + (p.default_forwarding_mode == MAP_FORWARDING_MODE_T ? "translation" : "encapsulation")); + printf(" Default forwarding rule : %s\n", + (p.default_forwarding_rule == MAP_FORWARDING_RULE_T ? "true" : "false")); + printf(" IPv6 fragment size : %d\n", p.ipv6_fragment_size); + printf(" IPv4 fragment inner : %s\n", + (p.ipv4_fragment_inner == MAP_IPV4_FRAG_INNER_T ? "true" : "false")); + printf(" NAPT always : %s\n", + (p.napt_always == MAP_NAPT_ALWAYS_T ? "true" : "false")); + printf(" NAPT force recycle : %s\n", + (p.napt_force_recycle == MAP_NAPT_FORCE_RECYCLE_T ? "true" : "false")); + if (cpp->has_bmr) { + printf(" Basic mapping rule :\n"); + printf(" Rule IPv6 prefix : %s/%d\n", + inet_ntop(AF_INET6, &cpp->bmrp.ipv6_prefix, s2, sizeof(s2)), cpp->bmrp.ipv6_prefix_length); + printf(" Rule IPv4 prefix : %s/%d\n", + format_host(AF_INET, 4, &cpp->bmrp.ipv4_prefix, s2, sizeof(s2)), cpp->bmrp.ipv4_prefix_length); + printf(" Rule PSID prefix : 0x%x/%d\n", + cpp->bmrp.psid_prefix, cpp->bmrp.psid_prefix_length); + printf(" EA-bits length : %d\n", cpp->bmrp.ea_length); + printf(" PSID offset : %d\n", cpp->bmrp.psid_offset); + printf(" Forwarding mode : %s\n", + (cpp->bmrp.forwarding_mode == MAP_FORWARDING_MODE_T ? "translation" : "encapsulation")); + printf(" Forwarding rule : %s\n", + (cpp->bmrp.forwarding_rule == MAP_FORWARDING_RULE_T ? "true" : "false")); + } else { + printf(" Basic mapping rule : (null)\n"); + } + printf(" MAP IPv6 address : %s/%d\n", + inet_ntop(AF_INET6, &cpp->map_ipv6_address, s2, sizeof(s2)), cpp->map_ipv6_address_length); + printf(" Shared IPv4 address : %s\n", + format_host(AF_INET, 4, &cpp->laddr4, s2, sizeof(s2))); + printf(" Assigned port-set ID : 0x%x/%d\n", + cpp->psid, cpp->psid_length); + printf(" Port-set :\n"); + num = cpp->port_range_length; + if (cp.port_range_length < cpp->port_range_length) + num = cp.port_range_length; + for (i = 0; i < num; ++i) { + printf(" Port-set #%04d : %6d(0x%04x) - %6d(0x%04x)\n", + i, + cpp->port_range[i].min, cpp->port_range[i].min, + cpp->port_range[i].max, cpp->port_range[i].max); + } + printf("\n"); + + free(cpp); + + return 0; +} + +static int do_show_rule(int argc, char **argv) +{ + struct map_parm p; + struct map_rule_parm rp; + struct map_pool_parm pp; + struct map_parm *pptr; + struct map_rule_parm *rptr; + char s1[1024]; + char s2[1024]; + int i; + + if (parse_args(argc, argv, SIOCGETMAP, &p, &rp, &pp) < 0) + return -1; + + if (map_ioctl(p.name, SIOCGETMAP, &p)) + return -1; + memset(s1, 0, 1024); + if_indextoname(p.tunnel_source, s1); + if (!s1[0]) + sprintf(s1, "(null)"); + + pptr = malloc(sizeof(struct map_parm) + sizeof(struct map_rule_parm) * p.rule_num); + if (!pptr) + return -1; + if (map_ioctl(p.name, SIOCGETMAPRULES, pptr)) + return -1; + printf("\n"); + printf(" Mode: 'E' = Encapsulation, 'T' = Translation. FMR: 'T' = FMR, '-' = Not FMR.\n"); + printf("\n"); + printf(" IPv6 prefix, IPv4 prefix, PSID prefix, EA-bits length, PSID offset, Mode, FMR.\n"); + for (i = 0; i < pptr->rule_num; i++) { + rptr = &pptr->rule[i]; + printf(" %7d: %26s/%-3d %15s/%-2d 0x%04x/%-2d %2d %2d %s %s\n", + i, + inet_ntop(AF_INET6, &rptr->ipv6_prefix, s1, sizeof(s1)), + rptr->ipv6_prefix_length, + format_host(AF_INET, 4, &rptr->ipv4_prefix, s2, sizeof(s2)), + rptr->ipv4_prefix_length, + rptr->psid_prefix, + rptr->psid_prefix_length, + rptr->ea_length, + rptr->psid_offset, + (rptr->forwarding_mode == MAP_FORWARDING_MODE_T ? "T" : "E"), + (rptr->forwarding_rule == MAP_FORWARDING_RULE_T ? "F" : "-")); + } + printf("\n"); + free(pptr); + + return 0; +} + + +#define ARRAY_MARGIN (256) +static int do_show_napt(int argc, char **argv) +{ + struct map_parm p; + struct map_rule_parm rp; + struct map_pool_parm pp; + struct map_napt_parm np, *npp; + char s1[64], s2[64], s3[64], s4[64], t[16], f[16], *proto; + struct tm tmd; + int i, num; + time_t now, last_used; + + if (parse_args(argc, argv, SIOCGETMAP, &p, &rp, &pp) < 0) + return -1; + + if (map_ioctl(p.name, SIOCGETMAP, &p)) + return -1; + + if (map_ioctl(p.name, SIOCGETMAPNAPTNUM, &np)) + return -1; + npp = malloc(sizeof(struct map_napt_parm) + sizeof(struct map_napt_node_parm) * (np.napt_node_num + ARRAY_MARGIN)); + if (!npp) + return -1; + npp->napt_node_num = np.napt_node_num + ARRAY_MARGIN; + if (map_ioctl(p.name, SIOCGETMAPNAPT, npp)) { + free(npp); + return -1; + } + + time(&now); + + num = npp->napt_node_num; + if (np.napt_node_num + ARRAY_MARGIN < npp->napt_node_num) + num = np.napt_node_num + ARRAY_MARGIN; + printf("\n"); + printf(" Proto: 'I' = ICMP, 'T' = TCP, 'U' = UDP.\n"); + printf(" Flags: SynOut, SynAckIn, AckOut, FinOut, FinAckIn, FinIn, FinAckOut, Rst.\n"); + printf(" '!' = Up, '.' = Down.\n"); + printf("\n"); + if (p.role == MAP_ROLE_CE) { + printf(" Last used, Local address:port, Mapped port, Remote address:port, Proto, Flags.\n"); + } else { + printf(" Last used, Local IPv6 address, Local address:port, Mapped address:port, Remote address:port, Proto, Flags.\n"); + } + for (i = 0; i < num; ++i) { + switch (npp->napt_node[i].proto) { + case IPPROTO_UDP: + proto = "U"; + break; + case IPPROTO_TCP: + proto = "T"; + break; + case IPPROTO_ICMP: + proto = "I"; + break; + default: + proto = "?"; + } + // npp->napt_node[i].flags + f[0] = (npp->napt_node[i].flags & 0x01) ? '!' : '.'; + f[1] = (npp->napt_node[i].flags & 0x02) ? '!' : '.'; + f[2] = (npp->napt_node[i].flags & 0x04) ? '!' : '.'; + f[3] = (npp->napt_node[i].flags & 0x08) ? '!' : '.'; + f[4] = (npp->napt_node[i].flags & 0x10) ? '!' : '.'; + f[5] = (npp->napt_node[i].flags & 0x20) ? '!' : '.'; + f[6] = (npp->napt_node[i].flags & 0x40) ? '!' : '.'; + f[7] = (npp->napt_node[i].flags & 0x80) ? '!' : '.'; + f[8] = '\0'; + // npp->napt_node[i].last_used + last_used = now - (npp->current_time.tv_sec - npp->napt_node[i].last_used.tv_sec); + localtime_r(&last_used, &tmd); + if (difftime(now, last_used) > 60 * 60 * 24) { + strftime(t, sizeof(t), "%y-%m-%d", &tmd); + } else { + strftime(t, sizeof(t), "%H:%M:%S", &tmd); + } + if (p.role == MAP_ROLE_CE) { + printf(" %8s %15s:%-5d %6d(0x%04x) %15s:%-5d %s %s\n", + t, + format_host(AF_INET, 4, &npp->napt_node[i].laddr, s1, sizeof(s1)), + ntohs(npp->napt_node[i].lport), + ntohs(npp->napt_node[i].mport), + ntohs(npp->napt_node[i].mport), + format_host(AF_INET, 4, &npp->napt_node[i].raddr, s2, sizeof(s2)), + ntohs(npp->napt_node[i].rport), + proto, + f); + } else { + printf(" %8s %-40s %15s:%-5d %15s:(0x%04x)%-6d %15s:%-5d %s %s\n", + t, + inet_ntop(AF_INET6, &npp->napt_node[i].laddr6, s1, sizeof(s1)), + format_host(AF_INET, 4, &npp->napt_node[i].laddr, s2, sizeof(s2)), + ntohs(npp->napt_node[i].lport), + format_host(AF_INET, 4, &npp->napt_node[i].maddr, s3, sizeof(s3)), + ntohs(npp->napt_node[i].mport), + ntohs(npp->napt_node[i].mport), + format_host(AF_INET, 4, &npp->napt_node[i].raddr, s4, sizeof(s4)), + ntohs(npp->napt_node[i].rport), + proto, + f); + } + } + printf("\n"); + + free(npp); + + return 0; +} + +static int do_add_rule(int argc, char **argv) +{ + struct map_parm p; + struct map_parm *pptr; + struct map_rule_parm *rpptr; + struct map_pool_parm pp; + + pptr = malloc(sizeof(struct map_parm) + sizeof(struct map_rule_parm)); + if (!pptr) + return -1; + rpptr = &pptr->rule[0]; + + //printf("pptr = %p, rpptr = %p, sizeof(*pptr) = %d.\n", pptr, rpptr, sizeof(*pptr)); + + if (parse_args(argc, argv, SIOCADDMAPRULES, pptr, rpptr, &pp) < 0) + return -1; + + if (!rpptr->forwarding_mode || !rpptr->forwarding_rule) { + if (map_ioctl(pptr->name, SIOCGETMAP, &p)) { + //printf("XXX\n"); + return -1; + } + if (!rpptr->forwarding_mode) + rpptr->forwarding_mode = p.default_forwarding_mode; + if (!rpptr->forwarding_rule) + rpptr->forwarding_rule = p.default_forwarding_rule; + } + + pptr->rule_num = 1; + pptr->pool_num = 0; + + if (map_ioctl(pptr->name, SIOCADDMAPRULES, pptr)) + return -1; + + return 0; +} + +static int do_del_rule(int argc, char **argv) +{ + struct map_parm *pptr; + struct map_rule_parm *rpptr; + struct map_pool_parm pp; + + pptr = malloc(sizeof(struct map_parm) + sizeof(struct map_rule_parm)); + if (!pptr) + return -1; + rpptr = &pptr->rule[0]; + + if (parse_args(argc, argv, SIOCDELMAPRULES, pptr, rpptr, &pp) < 0) + return -1; + + pptr->rule_num = 1; + pptr->pool_num = 0; + + if (map_ioctl(pptr->name, SIOCDELMAPRULES, pptr)) + return -1; + + return 0; +} + +static int do_add_pool(int argc, char **argv) +{ + struct map_parm p; + struct map_parm *pptr; + struct map_pool_parm *ppptr; + struct map_rule_parm rp; + + pptr = malloc(sizeof(struct map_parm) + sizeof(struct map_pool_parm)); + if (!pptr) + return -1; + ppptr = &pptr->pool[0]; + + if (parse_args(argc, argv, SIOCADDMAPPOOLS, pptr, &rp, ppptr) < 0) + return -1; + + if (map_ioctl(pptr->name, SIOCGETMAP, &p)) + return -1; + + if (p.role != MAP_ROLE_BR) + return -1; + + pptr->rule_num = 0; + pptr->pool_num = 1; + + if (map_ioctl(pptr->name, SIOCADDMAPPOOLS, pptr)) + return -1; + + return 0; +} + +static int do_del_pool(int argc, char **argv) +{ + struct map_parm p; + struct map_parm *pptr; + struct map_pool_parm *ppptr; + struct map_rule_parm rp; + + pptr = malloc(sizeof(struct map_parm) + sizeof(struct map_pool_parm)); + if (!pptr) + return -1; + ppptr = &pptr->pool[0]; + + if (parse_args(argc, argv, SIOCDELMAPPOOLS, pptr, &rp, ppptr) < 0) + return -1; + + if (map_ioctl(pptr->name, SIOCGETMAP, &p)) + return -1; + + if (p.role != MAP_ROLE_BR) + return -1; + + pptr->rule_num = 0; + pptr->pool_num = 1; + + if (map_ioctl(pptr->name, SIOCDELMAPPOOLS, pptr)) + return -1; + + return 0; +} + +int do_ipmap(int argc, char **argv) +{ + if (argc > 0) { + if (matches(*argv, "add") == 0) + return do_add(argc - 1, argv + 1); + if (matches(*argv, "change") == 0) + return do_change(argc - 1, argv + 1); + if (matches(*argv, "del") == 0) + return do_del(argc - 1, argv + 1); + if (matches(*argv, "add-rule") == 0) + return do_add_rule(argc - 1, argv + 1); + if (matches(*argv, "del-rule") == 0) + return do_del_rule(argc - 1, argv + 1); + if (matches(*argv, "add-pool") == 0) + return do_add_pool(argc - 1, argv + 1); + if (matches(*argv, "del-pool") == 0) + return do_del_pool(argc - 1, argv + 1); + if (matches(*argv, "show") == 0) + return do_show(argc - 1, argv + 1); + if (matches(*argv, "show-parm") == 0) + return do_show_parm(argc - 1, argv + 1); + if (matches(*argv, "show-rule") == 0) + return do_show_rule(argc - 1, argv + 1); + if (matches(*argv, "show-napt") == 0) + return do_show_napt(argc - 1, argv + 1); + if (matches(*argv, "help") == 0) + usage(); + } else + return do_show(0, NULL); + + fprintf(stderr, "Command \"%s\" is unknown, try \"ip map help\".\n", *argv); + exit(-1); +}