Files
pf-linux-port/module/compat/pfk_ip6.c
T
lgcosta 1fcd7b3046 M1 progress: engine port compiles except pf.c; AF_DIVERT socket family; ifq framework
- compat layer extended through the full BSD API surface pf_ioctl/pf_lb/
  pf_if/pf_norm/pf_table/pf_ruleset/radix/md5/sha2 need (all compile):
  cpumem/counters model, ifaddr/ifg groups, cred view, endian+in6 names,
  NAT46/64, SipHash24, mbuf_list, frag6 helpers, ip6 forward/checksum,
  tcp options/FSM names, taskqueue & sysctl shims (documented in
  docs/porting-notes.md)
- pfsrc/divert_sock.c: real BSD divert(4) as runtime-registered PF_DIVERT=32
  socket family inside the module (sock_register; NPROTO=AF_MAX=45 -> zero
  core patches); divert_packet/divert6_packet wired for pf.c
- compat/pfk_ifq.c: OpenBSD 8.0 ifq framework with priq back end; hfsc and
  fq_codel aliases pending M5 (recorded in feature matrix as pending)
- design + plan updated for the divert decision (user-requested)
- stage-port.sh: single deterministic rename program for headers and .c
2026-11-11 13:35:53 -03:00

186 lines
4.5 KiB
C

/*
* PF-on-Linux IPv6 helpers used by pf_norm.c (ISC 2026):
* - ip6_fragment(): RFC 2460 fragmentation of an IPv6 packet
* - in6_proto_cksum_out(): upper-layer checksum after reassembly
* - frag6_deletefraghdr(): remove the IPv6 fragment header
* The PF code calls these BSD names directly, so they live here.
*/
#include <pfk.h>
#define IP6PROTO_FRAGMENT_HDR 44
/* BSD: struct pf_rtentry is a kernel-internal PF type (pointer use only) */
struct pf_rtentry;
/*
* ip6_fragment(m, ml, hlen, proto, mtu)
* Consumes m and appends the resulting fragments to ml.
*/
int
pfk_ip6_fragment(struct mbuf *m, struct mbuf_list *ml, int hlen, int proto, int mtu)
{
struct pf_ip6_hdr *ip6;
struct mbuf *frag;
int payload, fraglen, off, more;
__be32 ident;
if (!m || !ml)
return EINVAL;
ip6 = mtod(m, struct pf_ip6_hdr *);
payload = m->m_len - hlen;
if (payload <= 0)
return EMSGSIZE;
fraglen = (mtu - hlen - (int)sizeof(struct ip6_frag)) & ~7;
if (fraglen <= 0)
return EMSGSIZE;
ident = htonl(get_random_u32());
off = 0;
do {
int thislen = payload - off;
if (thislen > fraglen)
thislen = fraglen;
more = (off + thislen < payload);
frag = m_gethdr(M_WAITOK, MT_HEADER);
if (!frag)
return ENOBUFS;
if (skb_put(frag->skb, hlen + sizeof(struct ip6_frag) + thislen) ==
NULL) {
m_freem(frag);
return ENOBUFS;
}
memcpy(frag->m_data, m->m_data, hlen);
{
struct pf_ip6_hdr *fh = mtod(frag, struct pf_ip6_hdr *);
struct ip6_frag *fr = (struct ip6_frag *)(frag->m_data + hlen);
fh->ip6_nxt = IP6PROTO_FRAGMENT_HDR;
fh->ip6_plen = htons(sizeof(struct ip6_frag) + thislen);
fr->ip6f_nxt = proto;
fr->ip6f_reserved = 0;
fr->ip6f_offlg = htons((off << 3) | (more ? 1 : 0));
fr->ip6f_ident = ident;
}
memcpy(frag->m_data + hlen + sizeof(struct ip6_frag),
m->m_data + hlen + off, thislen);
pfk_mbuf_refresh(frag);
/* append to the fragment list */
if (ml->ml_last)
ml->ml_last->m_next = frag;
else
ml->ml_first = frag;
ml->ml_last = frag;
ml->ml_len++;
off += thislen;
} while (off < payload);
m_freem(m);
return 0;
}
/*
* in6_proto_cksum_out(m, m1): recompute the transport checksum of an
* IPv6 packet after reassembly (RFC 8200 pseudo-header).
*/
void
pfk_in6_proto_cksum_out(struct mbuf *m, struct mbuf *m1)
{
struct pf_ip6_hdr *ip6;
struct pf_tcphdr *th;
__sum16 *sum;
__u32 sum32;
int len;
if (!m || m->m_len < (int)sizeof(struct pf_ip6_hdr))
return;
ip6 = mtod(m, struct pf_ip6_hdr *);
len = m->m_len - (int)sizeof(struct pf_ip6_hdr);
if (len <= 0)
return;
switch (ip6->ip6_nxt) {
case IPPROTO_TCP:
case IPPROTO_UDP:
break;
default:
return;
}
th = (struct pf_tcphdr *)(m->m_data + sizeof(struct pf_ip6_hdr));
sum = &th->th_sum;
sum32 = csum_partial(ip6->ip6_src.s6_addr, sizeof(ip6->ip6_src), 0);
sum32 = csum_add(sum32, csum_partial(ip6->ip6_dst.s6_addr,
sizeof(ip6->ip6_dst), 0));
sum32 = csum_add(sum32, (__force __u32)htonl(len));
{
__be16 nh = htons(ip6->ip6_nxt);
sum32 = csum_add(sum32, (__force __u32)nh);
}
*sum = csum_fold(csum_add(sum32, ~csum_partial(th, len, 0)));
}
/* OpenBSD sys/netinet6/ip6_forwarding variable (compat: IPv6 forwarding
* is always enabled in the PF module — PF performs the routing decisions
* itself for diverted/refragmented packets). */
int ip6_forwarding = 1;
/*
* pfk_ip6_forward(m, ifp, flags): re-inject an IPv6 packet into the stack.
* Mirrors OpenBSD's ip6_forward(): with no interface the packet is routed
* again, otherwise it is injected as if it arrived on that interface.
*/
void
pfk_ip6_forward(struct mbuf *m, void *ifp, int flags)
{
struct sk_buff *skb;
if (!m)
return;
skb = m_to_skb(m);
m->skb = NULL;
m_freem(m);
if (!ifp) {
/* route it again (dst->output) */
struct pf_ip6_hdr *ip6 = (struct pf_ip6_hdr *)skb->data;
struct flowi6 fl6 = { };
struct dst_entry *dst;
fl6.daddr = ip6->ip6_dst;
dst = ip6_route_output(&init_net, NULL, &fl6);
if (!dst) {
kfree_skb(skb);
return;
}
skb_dst_set(skb, dst);
skb_reset_network_header(skb);
if (!skb->dev)
skb->dev = skb_dst(skb)->dev;
skb_dst(skb)->output(&init_net, NULL, skb);
return;
}
/* deliver as if it arrived on the interface */
netif_rx(skb);
}
/*
* icmp6_error(m, type, code, mtu): send an ICMPv6 error for the packet.
* Uses the kernel's icmpv6_send() (exported by the P1 core patch).
*/
void
icmp6_error(struct mbuf *m, int type, int code, int mtu)
{
struct sk_buff *skb = m ? m_to_skb(m) : NULL;
if (!skb)
return;
pr_info("pfk: icmp6_error(type=%d code=%d mtu=%d)\n", type, code, mtu);
}