#include "opt_inet6.h"
#include "opt_rss.h"
#include <sys/param.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/priv.h>
#include <sys/kernel.h>
#include <sys/smp.h>
#include <sys/sysctl.h>
#include <sys/sbuf.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/netisr.h>
#include <net/rss_config.h>
#include <netinet/in.h>
#include <netinet/in_pcb.h>
#include <netinet6/in6_rss.h>
#include <netinet/in_var.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
uint32_t
rss_hash_ip6_2tuple(const struct in6_addr *src, const struct in6_addr *dst)
{
uint8_t data[sizeof(*src) + sizeof(*dst)];
u_int datalen;
datalen = 0;
bcopy(src, &data[datalen], sizeof(*src));
datalen += sizeof(*src);
bcopy(dst, &data[datalen], sizeof(*dst));
datalen += sizeof(*dst);
return (rss_hash(datalen, data));
}
uint32_t
rss_hash_ip6_4tuple(const struct in6_addr *src, u_short srcport,
const struct in6_addr *dst, u_short dstport)
{
uint8_t data[sizeof(*src) + sizeof(*dst) + sizeof(srcport) +
sizeof(dstport)];
u_int datalen;
datalen = 0;
bcopy(src, &data[datalen], sizeof(*src));
datalen += sizeof(*src);
bcopy(dst, &data[datalen], sizeof(*dst));
datalen += sizeof(*dst);
bcopy(&srcport, &data[datalen], sizeof(srcport));
datalen += sizeof(srcport);
bcopy(&dstport, &data[datalen], sizeof(dstport));
datalen += sizeof(dstport);
return (rss_hash(datalen, data));
}
int
rss_proto_software_hash_v6(const struct in6_addr *s, const struct in6_addr *d,
u_short sp, u_short dp, int proto,
uint32_t *hashval, uint32_t *hashtype)
{
uint32_t hash;
if ((proto == IPPROTO_TCP) &&
(rss_gethashconfig() & RSS_HASHTYPE_RSS_TCP_IPV6)) {
hash = rss_hash_ip6_4tuple(s, sp, d, dp);
*hashval = hash;
*hashtype = M_HASHTYPE_RSS_TCP_IPV6;
return (0);
} else if ((proto == IPPROTO_UDP) &&
(rss_gethashconfig() & RSS_HASHTYPE_RSS_UDP_IPV6)) {
hash = rss_hash_ip6_4tuple(s, sp, d, dp);
*hashval = hash;
*hashtype = M_HASHTYPE_RSS_UDP_IPV6;
return (0);
} else if (rss_gethashconfig() & RSS_HASHTYPE_RSS_IPV6) {
hash = rss_hash_ip6_2tuple(s, d);
*hashval = hash;
*hashtype = M_HASHTYPE_RSS_IPV6;
return (0);
}
RSS_DEBUG("no available hashtypes!\n");
return (-1);
}
uint32_t
xps_proto_software_hash_v6(const struct in6_addr *s, const struct in6_addr *d,
u_short sp, u_short dp, int proto, uint32_t *hashtype)
{
uint32_t hash;
if ((proto == IPPROTO_TCP) &&
(rss_gethashconfig() & RSS_HASHTYPE_RSS_TCP_IPV6)) {
hash = rss_hash_ip6_4tuple(d, dp, s, sp);
*hashtype = M_HASHTYPE_RSS_TCP_IPV6;
return (hash);
} else if ((proto == IPPROTO_UDP) &&
(rss_gethashconfig() & RSS_HASHTYPE_RSS_UDP_IPV6)) {
hash = rss_hash_ip6_4tuple(d, dp, s, sp);
*hashtype = M_HASHTYPE_RSS_UDP_IPV6;
return (hash);
} else if (rss_gethashconfig() & RSS_HASHTYPE_RSS_IPV6) {
hash = rss_hash_ip6_2tuple(d, s);
*hashtype = M_HASHTYPE_RSS_IPV6;
return (hash);
}
*hashtype = M_HASHTYPE_NONE;
return (0);
}
int
rss_mbuf_software_hash_v6(const struct mbuf *m, int dir, uint32_t *hashval,
uint32_t *hashtype)
{
const struct ip6_hdr *ip6;
const struct ip6_frag *ip6f;
const struct tcphdr *th;
const struct udphdr *uh;
uint32_t flowtype;
uint8_t proto;
int off, newoff;
int nxt;
if (dir != RSS_HASH_PKT_INGRESS) {
RSS_DEBUG("called on EGRESS packet!\n");
return (-1);
}
off = sizeof(struct ip6_hdr);
if (m->m_pkthdr.len < off) {
RSS_DEBUG("short mbuf pkthdr\n");
return (-1);
}
if (m->m_len < off) {
RSS_DEBUG("short mbuf len\n");
return (-1);
}
ip6 = mtod(m, struct ip6_hdr *);
proto = ip6->ip6_nxt;
while (proto != IPPROTO_FRAGMENT) {
newoff = ip6_nexthdr(m, off, proto, &nxt);
if (newoff < 0)
break;
off = newoff;
proto = nxt;
}
if (proto == IPPROTO_FRAGMENT) {
if (m->m_len < off + sizeof(struct ip6_frag)) {
RSS_DEBUG("short fragment frame?\n");
return (-1);
}
ip6f = (const struct ip6_frag *)((c_caddr_t)ip6 + off);
if ((ip6f->ip6f_offlg & ~IP6F_RESERVED_MASK) == 0) {
off = ip6_lasthdr(m, off, proto, &nxt);
if (off < 0) {
RSS_DEBUG("invalid extension header\n");
return (-1);
}
proto = nxt;
}
}
flowtype = M_HASHTYPE_GET(m);
if (flowtype != M_HASHTYPE_NONE) {
switch (proto) {
case IPPROTO_UDP:
if ((rss_gethashconfig() & RSS_HASHTYPE_RSS_UDP_IPV6) &&
(flowtype == M_HASHTYPE_RSS_UDP_IPV6)) {
return (1);
}
if ((rss_gethashconfig() & RSS_HASHTYPE_RSS_IPV6) &&
((rss_gethashconfig() & RSS_HASHTYPE_RSS_UDP_IPV6) == 0) &&
flowtype == M_HASHTYPE_RSS_IPV6) {
return (1);
}
break;
case IPPROTO_TCP:
if ((rss_gethashconfig() & RSS_HASHTYPE_RSS_TCP_IPV6) &&
(flowtype == M_HASHTYPE_RSS_TCP_IPV6)) {
return (1);
}
if ((rss_gethashconfig() & RSS_HASHTYPE_RSS_IPV6) &&
((rss_gethashconfig() & RSS_HASHTYPE_RSS_TCP_IPV6) == 0) &&
flowtype == M_HASHTYPE_RSS_IPV6) {
return (1);
}
break;
default:
if ((rss_gethashconfig() & RSS_HASHTYPE_RSS_IPV6) &&
flowtype == M_HASHTYPE_RSS_IPV6) {
return (1);
}
break;
}
}
if ((rss_gethashconfig() & RSS_HASHTYPE_RSS_TCP_IPV6) &&
(proto == IPPROTO_TCP)) {
if (m->m_len < off + sizeof(struct tcphdr)) {
RSS_DEBUG("short TCP frame?\n");
return (-1);
}
th = (const struct tcphdr *)((c_caddr_t)ip6 + off);
return rss_proto_software_hash_v6(&ip6->ip6_src, &ip6->ip6_dst,
th->th_sport,
th->th_dport,
proto,
hashval,
hashtype);
} else if ((rss_gethashconfig() & RSS_HASHTYPE_RSS_UDP_IPV6) &&
(proto == IPPROTO_UDP)) {
if (m->m_len < off + sizeof(struct udphdr)) {
RSS_DEBUG("short UDP frame?\n");
return (-1);
}
uh = (const struct udphdr *)((c_caddr_t)ip6 + off);
return rss_proto_software_hash_v6(&ip6->ip6_src, &ip6->ip6_dst,
uh->uh_sport,
uh->uh_dport,
proto,
hashval,
hashtype);
} else if (rss_gethashconfig() & RSS_HASHTYPE_RSS_IPV6) {
return rss_proto_software_hash_v6(&ip6->ip6_src, &ip6->ip6_dst,
0,
0,
0,
hashval,
hashtype);
} else {
RSS_DEBUG("no available hashtypes!\n");
return (-1);
}
}
#ifdef RSS
struct mbuf *
rss_soft_m2cpuid_v6(struct mbuf *m, uintptr_t source, u_int *cpuid)
{
uint32_t hash_val, hash_type;
int ret;
M_ASSERTPKTHDR(m);
ret = rss_mbuf_software_hash_v6(m, RSS_HASH_PKT_INGRESS,
&hash_val, &hash_type);
if (ret > 0) {
*cpuid = rss_hash2cpuid(m->m_pkthdr.flowid, M_HASHTYPE_GET(m));
} else if (ret == 0) {
m->m_pkthdr.flowid = hash_val;
M_HASHTYPE_SET(m, hash_type);
*cpuid = rss_hash2cpuid(m->m_pkthdr.flowid, M_HASHTYPE_GET(m));
} else {
*cpuid = NETISR_CPUID_NONE;
}
return (m);
}
#endif