Changeset View
Changeset View
Standalone View
Standalone View
sys/netpfil/ipfw/nat64/nat64clat.c
- This file was copied from sys/netpfil/ipfw/nat64/nat64stl.c.
/*- | /*- | ||||
* Copyright (c) 2015-2016 Yandex LLC | * SPDX-License-Identifier: BSD-2-Clause-FreeBSD | ||||
* Copyright (c) 2015-2016 Andrey V. Elsukov <ae@FreeBSD.org> | |||||
* All rights reserved. | |||||
* | * | ||||
* Copyright (c) 2019 Yandex LLC | |||||
* Copyright (c) 2019 Andrey V. Elsukov <ae@FreeBSD.org> | |||||
* Copyright (c) 2019 Boris N. Lytochkin <lytboris@gmail.com> | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
* modification, are permitted provided that the following conditions | * modification, are permitted provided that the following conditions | ||||
* are met: | * are met: | ||||
* | * | ||||
* 1. Redistributions of source code must retain the above copyright | * 1. Redistributions of source code must retain the above copyright | ||||
* notice, this list of conditions and the following disclaimer. | * notice, this list of conditions and the following disclaimer. | ||||
* 2. Redistributions in binary form must reproduce the above copyright | * 2. Redistributions in binary form must reproduce the above copyright | ||||
* notice, this list of conditions and the following disclaimer in the | * notice, this list of conditions and the following disclaimer in the | ||||
Show All 38 Lines | |||||
#include <netinet/ip_fw.h> | #include <netinet/ip_fw.h> | ||||
#include <netinet/ip6.h> | #include <netinet/ip6.h> | ||||
#include <netinet/icmp6.h> | #include <netinet/icmp6.h> | ||||
#include <netinet6/ip_fw_nat64.h> | #include <netinet6/ip_fw_nat64.h> | ||||
#include <netpfil/ipfw/ip_fw_private.h> | #include <netpfil/ipfw/ip_fw_private.h> | ||||
#include <netpfil/pf/pf.h> | #include <netpfil/pf/pf.h> | ||||
#include "nat64stl.h" | #include "nat64clat.h" | ||||
#define NAT64_LOOKUP(chain, cmd) \ | #define NAT64_LOOKUP(chain, cmd) \ | ||||
(struct nat64stl_cfg *)SRV_OBJECT((chain), (cmd)->arg1) | (struct nat64clat_cfg *)SRV_OBJECT((chain), (cmd)->arg1) | ||||
static void | static void | ||||
nat64stl_log(struct pfloghdr *plog, struct mbuf *m, sa_family_t family, | nat64clat_log(struct pfloghdr *plog, struct mbuf *m, sa_family_t family, | ||||
uint32_t kidx) | uint32_t kidx) | ||||
{ | { | ||||
static uint32_t pktid = 0; | static uint32_t pktid = 0; | ||||
memset(plog, 0, sizeof(*plog)); | memset(plog, 0, sizeof(*plog)); | ||||
plog->length = PFLOG_REAL_HDRLEN; | plog->length = PFLOG_REAL_HDRLEN; | ||||
plog->af = family; | plog->af = family; | ||||
plog->action = PF_NAT; | plog->action = PF_NAT; | ||||
plog->dir = PF_IN; | plog->dir = PF_IN; | ||||
plog->rulenr = htonl(kidx); | plog->rulenr = htonl(kidx); | ||||
pktid++; | pktid++; | ||||
plog->subrulenr = htonl(pktid); | plog->subrulenr = htonl(pktid); | ||||
plog->ruleset[0] = '\0'; | plog->ruleset[0] = '\0'; | ||||
strlcpy(plog->ifname, "NAT64STL", sizeof(plog->ifname)); | strlcpy(plog->ifname, "NAT64CLAT", sizeof(plog->ifname)); | ||||
ipfw_bpf_mtap2(plog, PFLOG_HDRLEN, m); | ipfw_bpf_mtap2(plog, PFLOG_HDRLEN, m); | ||||
} | } | ||||
static int | static int | ||||
nat64stl_handle_ip4(struct ip_fw_chain *chain, struct nat64stl_cfg *cfg, | nat64clat_handle_ip4(struct ip_fw_chain *chain, struct nat64clat_cfg *cfg, | ||||
struct mbuf *m, uint32_t tablearg) | struct mbuf *m) | ||||
{ | { | ||||
struct pfloghdr loghdr, *logdata; | struct pfloghdr loghdr, *logdata; | ||||
struct in6_addr saddr, daddr; | struct in6_addr saddr, daddr; | ||||
struct ip *ip; | struct ip *ip; | ||||
ip = mtod(m, struct ip*); | ip = mtod(m, struct ip*); | ||||
/* source address for CLAT may be private with no harm */ | |||||
if (nat64_check_ip4(ip->ip_src.s_addr) != 0 || | if (nat64_check_ip4(ip->ip_src.s_addr) != 0 || | ||||
nat64_check_ip4(ip->ip_dst.s_addr) != 0 || | nat64_check_ip4(ip->ip_dst.s_addr) != 0 || | ||||
nat64_check_private_ip4(&cfg->base, ip->ip_src.s_addr) != 0 || | |||||
nat64_check_private_ip4(&cfg->base, ip->ip_dst.s_addr) != 0) | nat64_check_private_ip4(&cfg->base, ip->ip_dst.s_addr) != 0) | ||||
return (NAT64SKIP); | return (NAT64SKIP); | ||||
daddr = TARG_VAL(chain, tablearg, nh6); | memcpy(&saddr, &cfg->base.clat_prefix, sizeof(saddr)); | ||||
if (nat64_check_ip6(&daddr) != 0) | nat64_embed_ip4(&saddr, cfg->base.clat_plen, ip->ip_src.s_addr); | ||||
return (NAT64MFREE); | memcpy(&daddr, &cfg->base.plat_prefix, sizeof(daddr)); | ||||
nat64_embed_ip4(&cfg->base, ip->ip_src.s_addr, &saddr); | nat64_embed_ip4(&daddr, cfg->base.plat_plen, ip->ip_dst.s_addr); | ||||
if (cfg->base.flags & NAT64_LOG) { | if (cfg->base.flags & NAT64_LOG) { | ||||
logdata = &loghdr; | logdata = &loghdr; | ||||
nat64stl_log(logdata, m, AF_INET, cfg->no.kidx); | nat64clat_log(logdata, m, AF_INET, cfg->no.kidx); | ||||
} else | } else | ||||
logdata = NULL; | logdata = NULL; | ||||
return (nat64_do_handle_ip4(m, &saddr, &daddr, 0, &cfg->base, | return (nat64_do_handle_ip4(m, &saddr, &daddr, 0, &cfg->base, | ||||
logdata)); | logdata)); | ||||
} | } | ||||
static int | static int | ||||
nat64stl_handle_ip6(struct ip_fw_chain *chain, struct nat64stl_cfg *cfg, | nat64clat_handle_ip6(struct ip_fw_chain *chain, struct nat64clat_cfg *cfg, | ||||
struct mbuf *m, uint32_t tablearg) | struct mbuf *m) | ||||
{ | { | ||||
struct pfloghdr loghdr, *logdata; | struct pfloghdr loghdr, *logdata; | ||||
struct ip6_hdr *ip6; | struct ip6_hdr *ip6; | ||||
uint32_t aaddr; | uint32_t aaddr; | ||||
aaddr = htonl(TARG_VAL(chain, tablearg, nh4)); | |||||
/* | /* | ||||
* NOTE: we expect ipfw_chk() did m_pullup() up to upper level | * NOTE: we expect ipfw_chk() did m_pullup() up to upper level | ||||
* protocol's headers. Also we skip some checks, that ip6_input(), | * protocol's headers. Also we skip some checks, that ip6_input(), | ||||
* ip6_forward(), ip6_fastfwd() and ipfw_chk() already did. | * ip6_forward(), ip6_fastfwd() and ipfw_chk() already did. | ||||
*/ | */ | ||||
ip6 = mtod(m, struct ip6_hdr *); | ip6 = mtod(m, struct ip6_hdr *); | ||||
/* Check ip6_dst matches configured prefix */ | /* Check ip6_dst matches configured prefix */ | ||||
if (bcmp(&ip6->ip6_dst, &cfg->base.prefix6, cfg->base.plen6 / 8) != 0) | if (memcmp(&ip6->ip6_dst, &cfg->base.clat_prefix, | ||||
cfg->base.clat_plen / 8) != 0) | |||||
return (NAT64SKIP); | return (NAT64SKIP); | ||||
/* Check ip6_src matches configured prefix */ | |||||
if (memcmp(&ip6->ip6_src, &cfg->base.plat_prefix, | |||||
cfg->base.plat_plen / 8) != 0) | |||||
return (NAT64SKIP); | |||||
if (cfg->base.flags & NAT64_LOG) { | if (cfg->base.flags & NAT64_LOG) { | ||||
logdata = &loghdr; | logdata = &loghdr; | ||||
nat64stl_log(logdata, m, AF_INET6, cfg->no.kidx); | nat64clat_log(logdata, m, AF_INET6, cfg->no.kidx); | ||||
} else | } else | ||||
logdata = NULL; | logdata = NULL; | ||||
aaddr = nat64_extract_ip4(&ip6->ip6_src, cfg->base.plat_plen); | |||||
return (nat64_do_handle_ip6(m, aaddr, 0, &cfg->base, logdata)); | return (nat64_do_handle_ip6(m, aaddr, 0, &cfg->base, logdata)); | ||||
} | } | ||||
static int | static int | ||||
nat64stl_handle_icmp6(struct ip_fw_chain *chain, struct nat64stl_cfg *cfg, | nat64clat_handle_icmp6(struct ip_fw_chain *chain, struct nat64clat_cfg *cfg, | ||||
struct mbuf *m) | struct mbuf *m) | ||||
{ | { | ||||
struct pfloghdr loghdr, *logdata; | struct pfloghdr loghdr, *logdata; | ||||
struct nat64_counters *stats; | struct nat64_counters *stats; | ||||
struct ip6_hdr *ip6i; | struct ip6_hdr *ip6i; | ||||
struct icmp6_hdr *icmp6; | struct icmp6_hdr *icmp6; | ||||
uint32_t tablearg; | uint32_t daddr; | ||||
int hlen, proto; | int hlen, proto; | ||||
hlen = 0; | hlen = 0; | ||||
stats = &cfg->base.stats; | stats = &cfg->base.stats; | ||||
proto = nat64_getlasthdr(m, &hlen); | proto = nat64_getlasthdr(m, &hlen); | ||||
if (proto != IPPROTO_ICMPV6) { | if (proto != IPPROTO_ICMPV6) { | ||||
NAT64STAT_INC(stats, dropped); | NAT64STAT_INC(stats, dropped); | ||||
return (NAT64MFREE); | return (NAT64MFREE); | ||||
Show All 20 Lines | if (m == NULL) { | ||||
NAT64STAT_INC(stats, nomem); | NAT64STAT_INC(stats, nomem); | ||||
return (NAT64RETURN); | return (NAT64RETURN); | ||||
} | } | ||||
/* | /* | ||||
* Use destination address from inner IPv6 header to determine | * Use destination address from inner IPv6 header to determine | ||||
* IPv4 mapped address. | * IPv4 mapped address. | ||||
*/ | */ | ||||
ip6i = mtodo(m, hlen); | ip6i = mtodo(m, hlen); | ||||
if (ipfw_lookup_table(chain, cfg->map64, | daddr = nat64_extract_ip4(&ip6i->ip6_dst, cfg->base.clat_plen); | ||||
sizeof(struct in6_addr), &ip6i->ip6_dst, &tablearg) == 0) { | if (daddr == 0) { | ||||
m_freem(m); | NAT64STAT_INC(stats, dropped); | ||||
return (NAT64RETURN); | return (NAT64MFREE); | ||||
} | } | ||||
if (cfg->base.flags & NAT64_LOG) { | if (cfg->base.flags & NAT64_LOG) { | ||||
logdata = &loghdr; | logdata = &loghdr; | ||||
nat64stl_log(logdata, m, AF_INET6, cfg->no.kidx); | nat64clat_log(logdata, m, AF_INET6, cfg->no.kidx); | ||||
} else | } else | ||||
logdata = NULL; | logdata = NULL; | ||||
return (nat64_handle_icmp6(m, 0, | return (nat64_handle_icmp6(m, 0, daddr, 0, &cfg->base, logdata)); | ||||
htonl(TARG_VAL(chain, tablearg, nh4)), 0, &cfg->base, logdata)); | |||||
} | } | ||||
int | int | ||||
ipfw_nat64stl(struct ip_fw_chain *chain, struct ip_fw_args *args, | ipfw_nat64clat(struct ip_fw_chain *chain, struct ip_fw_args *args, | ||||
ipfw_insn *cmd, int *done) | ipfw_insn *cmd, int *done) | ||||
{ | { | ||||
ipfw_insn *icmd; | ipfw_insn *icmd; | ||||
struct nat64stl_cfg *cfg; | struct nat64clat_cfg *cfg; | ||||
in_addr_t dst4; | |||||
uint32_t tablearg; | |||||
int ret; | int ret; | ||||
IPFW_RLOCK_ASSERT(chain); | IPFW_RLOCK_ASSERT(chain); | ||||
*done = 0; /* try next rule if not matched */ | *done = 0; /* try next rule if not matched */ | ||||
icmd = cmd + 1; | icmd = cmd + 1; | ||||
if (cmd->opcode != O_EXTERNAL_ACTION || | if (cmd->opcode != O_EXTERNAL_ACTION || | ||||
cmd->arg1 != V_nat64stl_eid || | cmd->arg1 != V_nat64clat_eid || | ||||
icmd->opcode != O_EXTERNAL_INSTANCE || | icmd->opcode != O_EXTERNAL_INSTANCE || | ||||
(cfg = NAT64_LOOKUP(chain, icmd)) == NULL) | (cfg = NAT64_LOOKUP(chain, icmd)) == NULL) | ||||
return (0); | return (0); | ||||
switch (args->f_id.addr_type) { | switch (args->f_id.addr_type) { | ||||
case 4: | case 4: | ||||
dst4 = htonl(args->f_id.dst_ip); | ret = nat64clat_handle_ip4(chain, cfg, args->m); | ||||
ret = ipfw_lookup_table(chain, cfg->map46, sizeof(in_addr_t), | |||||
&dst4, &tablearg); | |||||
break; | break; | ||||
case 6: | case 6: | ||||
ret = ipfw_lookup_table(chain, cfg->map64, | ret = nat64clat_handle_ip6(chain, cfg, args->m); | ||||
sizeof(struct in6_addr), &args->f_id.src_ip6, &tablearg); | |||||
break; | break; | ||||
default: | default: | ||||
return (0); | return (0); | ||||
} | } | ||||
if (ret == 0) { | |||||
if (ret == NAT64SKIP) { | |||||
/* | /* | ||||
* In case when packet is ICMPv6 message from an intermediate | * In case when packet is ICMPv6 message from an intermediate | ||||
* router, the source address of message will not match the | * router, the source address of message will not match the | ||||
* addresses from our map64 table. | * addresses from configured prefixes. | ||||
*/ | */ | ||||
if (args->f_id.proto != IPPROTO_ICMPV6) | if (args->f_id.proto != IPPROTO_ICMPV6) | ||||
return (0); | return (0); | ||||
ret = nat64stl_handle_icmp6(chain, cfg, args->m); | ret = nat64clat_handle_icmp6(chain, cfg, args->m); | ||||
} else { | |||||
if (args->f_id.addr_type == 4) | |||||
ret = nat64stl_handle_ip4(chain, cfg, args->m, | |||||
tablearg); | |||||
else | |||||
ret = nat64stl_handle_ip6(chain, cfg, args->m, | |||||
tablearg); | |||||
} | } | ||||
if (ret == NAT64SKIP) | if (ret == NAT64SKIP) | ||||
return (0); | return (0); | ||||
*done = 1; /* terminate the search */ | *done = 1; /* terminate the search */ | ||||
if (ret == NAT64MFREE) | if (ret == NAT64MFREE) | ||||
m_freem(args->m); | m_freem(args->m); | ||||
args->m = NULL; | args->m = NULL; | ||||
return (IP_FW_DENY); | return (IP_FW_NAT64); | ||||
} | } | ||||