1303612Sjulian/*- 2303612Sjulian * Copyright (c) 2015 Dmitry Vagin <daemon.hammer@ya.ru> 3303612Sjulian * All rights reserved. 4303612Sjulian * 5303612Sjulian * Redistribution and use in source and binary forms, with or without 6303612Sjulian * modification, are permitted provided that the following conditions 7303612Sjulian * are met: 8303612Sjulian * 1. Redistributions of source code must retain the above copyright 9303612Sjulian * notice, this list of conditions and the following disclaimer. 10303612Sjulian * 2. Redistributions in binary form must reproduce the above copyright 11303612Sjulian * notice, this list of conditions and the following disclaimer in the 12303612Sjulian * documentation and/or other materials provided with the distribution. 13303612Sjulian * 14303612Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15303612Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16303612Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17303612Sjulian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18303612Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19303612Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20303612Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21303612Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22303612Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23303612Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24303612Sjulian * SUCH DAMAGE. 25303612Sjulian * 26303612Sjulian */ 27303612Sjulian 28303612Sjulian#include <sys/cdefs.h> 29303612Sjulian__FBSDID("$FreeBSD: stable/10/sys/netgraph/ng_checksum.c 309387 2016-12-02 05:38:25Z julian $"); 30303612Sjulian 31303612Sjulian#include "opt_inet.h" 32303612Sjulian#include "opt_inet6.h" 33303612Sjulian 34303612Sjulian#include <sys/param.h> 35303612Sjulian#include <sys/systm.h> 36303612Sjulian#include <sys/kernel.h> 37303612Sjulian#include <sys/endian.h> 38303612Sjulian#include <sys/malloc.h> 39303612Sjulian#include <sys/mbuf.h> 40303612Sjulian#include <sys/socket.h> 41303612Sjulian 42303612Sjulian#include <net/bpf.h> 43303612Sjulian#include <net/ethernet.h> 44303612Sjulian#include <net/if.h> 45303612Sjulian#include <net/if_vlan_var.h> 46303612Sjulian 47303612Sjulian#include <netinet/in.h> 48303612Sjulian#include <netinet/ip.h> 49303612Sjulian#include <netinet/ip6.h> 50303612Sjulian#include <netinet/tcp.h> 51303612Sjulian#include <netinet/udp.h> 52303612Sjulian#include <machine/in_cksum.h> 53303612Sjulian 54303612Sjulian#include <netgraph/ng_message.h> 55303612Sjulian#include <netgraph/ng_parse.h> 56303612Sjulian#include <netgraph/netgraph.h> 57303612Sjulian 58303612Sjulian#include <netgraph/ng_checksum.h> 59303612Sjulian 60303612Sjulian/* private data */ 61303612Sjulianstruct ng_checksum_priv { 62303612Sjulian hook_p in; 63303612Sjulian hook_p out; 64303612Sjulian uint8_t dlt; /* DLT_XXX from bpf.h */ 65303612Sjulian struct ng_checksum_config *conf; 66303612Sjulian struct ng_checksum_stats stats; 67303612Sjulian}; 68303612Sjulian 69303612Sjuliantypedef struct ng_checksum_priv *priv_p; 70303612Sjulian 71303612Sjulian/* Netgraph methods */ 72303612Sjulianstatic ng_constructor_t ng_checksum_constructor; 73303612Sjulianstatic ng_rcvmsg_t ng_checksum_rcvmsg; 74303612Sjulianstatic ng_shutdown_t ng_checksum_shutdown; 75303612Sjulianstatic ng_newhook_t ng_checksum_newhook; 76303612Sjulianstatic ng_rcvdata_t ng_checksum_rcvdata; 77303612Sjulianstatic ng_disconnect_t ng_checksum_disconnect; 78303612Sjulian 79303612Sjulian#define ERROUT(x) { error = (x); goto done; } 80303612Sjulian 81303612Sjulianstatic const struct ng_parse_struct_field ng_checksum_config_type_fields[] 82303612Sjulian = NG_CHECKSUM_CONFIG_TYPE; 83303612Sjulianstatic const struct ng_parse_type ng_checksum_config_type = { 84303612Sjulian &ng_parse_struct_type, 85303612Sjulian &ng_checksum_config_type_fields 86303612Sjulian}; 87303612Sjulian 88303612Sjulianstatic const struct ng_parse_struct_field ng_checksum_stats_fields[] 89303612Sjulian = NG_CHECKSUM_STATS_TYPE; 90303612Sjulianstatic const struct ng_parse_type ng_checksum_stats_type = { 91303612Sjulian &ng_parse_struct_type, 92303612Sjulian &ng_checksum_stats_fields 93303612Sjulian}; 94303612Sjulian 95303612Sjulianstatic const struct ng_cmdlist ng_checksum_cmdlist[] = { 96303612Sjulian { 97303612Sjulian NGM_CHECKSUM_COOKIE, 98303612Sjulian NGM_CHECKSUM_GETDLT, 99303612Sjulian "getdlt", 100303612Sjulian NULL, 101303612Sjulian &ng_parse_uint8_type 102303612Sjulian }, 103303612Sjulian { 104303612Sjulian NGM_CHECKSUM_COOKIE, 105303612Sjulian NGM_CHECKSUM_SETDLT, 106303612Sjulian "setdlt", 107303612Sjulian &ng_parse_uint8_type, 108303612Sjulian NULL 109303612Sjulian }, 110303612Sjulian { 111303612Sjulian NGM_CHECKSUM_COOKIE, 112303612Sjulian NGM_CHECKSUM_GETCONFIG, 113303612Sjulian "getconfig", 114303612Sjulian NULL, 115303612Sjulian &ng_checksum_config_type 116303612Sjulian }, 117303612Sjulian { 118303612Sjulian NGM_CHECKSUM_COOKIE, 119303612Sjulian NGM_CHECKSUM_SETCONFIG, 120303612Sjulian "setconfig", 121303612Sjulian &ng_checksum_config_type, 122303612Sjulian NULL 123303612Sjulian }, 124303612Sjulian { 125303612Sjulian NGM_CHECKSUM_COOKIE, 126303612Sjulian NGM_CHECKSUM_GET_STATS, 127303612Sjulian "getstats", 128303612Sjulian NULL, 129303612Sjulian &ng_checksum_stats_type 130303612Sjulian }, 131303612Sjulian { 132303612Sjulian NGM_CHECKSUM_COOKIE, 133303612Sjulian NGM_CHECKSUM_CLR_STATS, 134303612Sjulian "clrstats", 135303612Sjulian NULL, 136303612Sjulian NULL 137303612Sjulian }, 138303612Sjulian { 139303612Sjulian NGM_CHECKSUM_COOKIE, 140303612Sjulian NGM_CHECKSUM_GETCLR_STATS, 141303612Sjulian "getclrstats", 142303612Sjulian NULL, 143303612Sjulian &ng_checksum_stats_type 144303612Sjulian }, 145303612Sjulian { 0 } 146303612Sjulian}; 147303612Sjulian 148303612Sjulianstatic struct ng_type typestruct = { 149303612Sjulian .version = NG_ABI_VERSION, 150303612Sjulian .name = NG_CHECKSUM_NODE_TYPE, 151303612Sjulian .constructor = ng_checksum_constructor, 152303612Sjulian .rcvmsg = ng_checksum_rcvmsg, 153303612Sjulian .shutdown = ng_checksum_shutdown, 154303612Sjulian .newhook = ng_checksum_newhook, 155303612Sjulian .rcvdata = ng_checksum_rcvdata, 156303612Sjulian .disconnect = ng_checksum_disconnect, 157303612Sjulian .cmdlist = ng_checksum_cmdlist, 158303612Sjulian}; 159303612Sjulian 160303612SjulianNETGRAPH_INIT(checksum, &typestruct); 161303612Sjulian 162303612Sjulianstatic int 163303612Sjulianng_checksum_constructor(node_p node) 164303612Sjulian{ 165303612Sjulian priv_p priv; 166303612Sjulian 167303612Sjulian priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK|M_ZERO); 168303612Sjulian priv->dlt = DLT_RAW; 169303612Sjulian 170303612Sjulian NG_NODE_SET_PRIVATE(node, priv); 171303612Sjulian 172303612Sjulian return (0); 173303612Sjulian} 174303612Sjulian 175303612Sjulianstatic int 176303612Sjulianng_checksum_newhook(node_p node, hook_p hook, const char *name) 177303612Sjulian{ 178303612Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 179303612Sjulian 180303612Sjulian if (strncmp(name, NG_CHECKSUM_HOOK_IN, strlen(NG_CHECKSUM_HOOK_IN)) == 0) { 181303612Sjulian priv->in = hook; 182303612Sjulian } else if (strncmp(name, NG_CHECKSUM_HOOK_OUT, strlen(NG_CHECKSUM_HOOK_OUT)) == 0) { 183303612Sjulian priv->out = hook; 184303612Sjulian } else 185303612Sjulian return (EINVAL); 186303612Sjulian 187303612Sjulian return (0); 188303612Sjulian} 189303612Sjulian 190303612Sjulianstatic int 191303612Sjulianng_checksum_rcvmsg(node_p node, item_p item, hook_p lasthook) 192303612Sjulian{ 193303612Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 194303612Sjulian struct ng_checksum_config *conf, *newconf; 195303612Sjulian struct ng_mesg *msg; 196303612Sjulian struct ng_mesg *resp = NULL; 197303612Sjulian int error = 0; 198303612Sjulian 199303612Sjulian NGI_GET_MSG(item, msg); 200303612Sjulian 201303612Sjulian if (msg->header.typecookie != NGM_CHECKSUM_COOKIE) 202303612Sjulian ERROUT(EINVAL); 203303612Sjulian 204303612Sjulian switch (msg->header.cmd) 205303612Sjulian { 206303612Sjulian case NGM_CHECKSUM_GETDLT: 207303612Sjulian NG_MKRESPONSE(resp, msg, sizeof(uint8_t), M_WAITOK); 208303612Sjulian 209303612Sjulian if (resp == NULL) 210303612Sjulian ERROUT(ENOMEM); 211303612Sjulian 212303612Sjulian *((uint8_t *) resp->data) = priv->dlt; 213303612Sjulian 214303612Sjulian break; 215303612Sjulian 216303612Sjulian case NGM_CHECKSUM_SETDLT: 217303612Sjulian if (msg->header.arglen != sizeof(uint8_t)) 218303612Sjulian ERROUT(EINVAL); 219303612Sjulian 220303612Sjulian switch (*(uint8_t *) msg->data) 221303612Sjulian { 222303612Sjulian case DLT_EN10MB: 223303612Sjulian case DLT_RAW: 224303612Sjulian priv->dlt = *(uint8_t *) msg->data; 225303612Sjulian break; 226303612Sjulian 227303612Sjulian default: 228303612Sjulian ERROUT(EINVAL); 229303612Sjulian } 230303612Sjulian 231303612Sjulian break; 232303612Sjulian 233303612Sjulian case NGM_CHECKSUM_GETCONFIG: 234303612Sjulian if (priv->conf == NULL) 235303612Sjulian ERROUT(0); 236303612Sjulian 237303612Sjulian NG_MKRESPONSE(resp, msg, sizeof(struct ng_checksum_config), M_WAITOK); 238303612Sjulian 239303612Sjulian if (resp == NULL) 240303612Sjulian ERROUT(ENOMEM); 241303612Sjulian 242303612Sjulian bcopy(priv->conf, resp->data, sizeof(struct ng_checksum_config)); 243303612Sjulian 244303612Sjulian break; 245303612Sjulian 246303612Sjulian case NGM_CHECKSUM_SETCONFIG: 247303612Sjulian conf = (struct ng_checksum_config *) msg->data; 248303612Sjulian 249303612Sjulian if (msg->header.arglen != sizeof(struct ng_checksum_config)) 250303612Sjulian ERROUT(EINVAL); 251303612Sjulian 252303612Sjulian conf->csum_flags &= NG_CHECKSUM_CSUM_IPV4|NG_CHECKSUM_CSUM_IPV6; 253303612Sjulian conf->csum_offload &= NG_CHECKSUM_CSUM_IPV4|NG_CHECKSUM_CSUM_IPV6; 254303612Sjulian 255303612Sjulian newconf = malloc(sizeof(struct ng_checksum_config), M_NETGRAPH, M_WAITOK|M_ZERO); 256303612Sjulian 257303612Sjulian bcopy(conf, newconf, sizeof(struct ng_checksum_config)); 258303612Sjulian 259303612Sjulian if (priv->conf) 260303612Sjulian free(priv->conf, M_NETGRAPH); 261303612Sjulian 262303612Sjulian priv->conf = newconf; 263303612Sjulian 264303612Sjulian break; 265303612Sjulian 266303612Sjulian case NGM_CHECKSUM_GET_STATS: 267303612Sjulian case NGM_CHECKSUM_CLR_STATS: 268303612Sjulian case NGM_CHECKSUM_GETCLR_STATS: 269303612Sjulian if (msg->header.cmd != NGM_CHECKSUM_CLR_STATS) { 270303612Sjulian NG_MKRESPONSE(resp, msg, sizeof(struct ng_checksum_stats), M_WAITOK); 271303612Sjulian 272303612Sjulian if (resp == NULL) 273303612Sjulian ERROUT(ENOMEM); 274303612Sjulian 275303612Sjulian bcopy(&(priv->stats), resp->data, sizeof(struct ng_checksum_stats)); 276303612Sjulian } 277303612Sjulian 278303612Sjulian if (msg->header.cmd != NGM_CHECKSUM_GET_STATS) 279303612Sjulian bzero(&(priv->stats), sizeof(struct ng_checksum_stats)); 280303612Sjulian 281303612Sjulian break; 282303612Sjulian 283303612Sjulian default: 284303612Sjulian ERROUT(EINVAL); 285303612Sjulian } 286303612Sjulian 287303612Sjuliandone: 288303612Sjulian NG_RESPOND_MSG(error, node, item, resp); 289303612Sjulian NG_FREE_MSG(msg); 290303612Sjulian 291303612Sjulian return (error); 292303612Sjulian} 293303612Sjulian 294303612Sjulian#define PULLUP_CHECK(mbuf, length) do { \ 295303612Sjulian pullup_len += length; \ 296303612Sjulian if (((mbuf)->m_pkthdr.len < pullup_len) || \ 297303612Sjulian (pullup_len > MHLEN)) { \ 298303612Sjulian return (EINVAL); \ 299303612Sjulian } \ 300303612Sjulian if ((mbuf)->m_len < pullup_len && \ 301303612Sjulian (((mbuf) = m_pullup((mbuf), pullup_len)) == NULL)) { \ 302303612Sjulian return (ENOBUFS); \ 303303612Sjulian } \ 304303612Sjulian} while (0) 305303612Sjulian 306303612Sjulian#ifdef INET 307303612Sjulianstatic int 308303612Sjulianchecksum_ipv4(priv_p priv, struct mbuf *m, int l3_offset) 309303612Sjulian{ 310303612Sjulian struct ip *ip4; 311303612Sjulian int pullup_len; 312303612Sjulian int hlen, plen; 313303612Sjulian int processed = 0; 314303612Sjulian 315303612Sjulian pullup_len = l3_offset; 316303612Sjulian 317303612Sjulian PULLUP_CHECK(m, sizeof(struct ip)); 318303612Sjulian ip4 = (struct ip *) mtodo(m, l3_offset); 319303612Sjulian 320303612Sjulian if (ip4->ip_v != IPVERSION) 321303612Sjulian return (EOPNOTSUPP); 322303612Sjulian 323303612Sjulian hlen = ip4->ip_hl << 2; 324303612Sjulian plen = ntohs(ip4->ip_len); 325303612Sjulian 326303612Sjulian if (hlen < sizeof(struct ip) || m->m_pkthdr.len < l3_offset + plen) 327303612Sjulian return (EINVAL); 328303612Sjulian 329303612Sjulian if (m->m_pkthdr.csum_flags & CSUM_IP) { 330303612Sjulian ip4->ip_sum = 0; 331303612Sjulian 332303612Sjulian if ((priv->conf->csum_offload & CSUM_IP) == 0) { 333303612Sjulian if (hlen == sizeof(struct ip)) 334303612Sjulian ip4->ip_sum = in_cksum_hdr(ip4); 335303612Sjulian else 336303612Sjulian ip4->ip_sum = in_cksum_skip(m, l3_offset + hlen, l3_offset); 337303612Sjulian 338303612Sjulian m->m_pkthdr.csum_flags &= ~CSUM_IP; 339303612Sjulian } 340303612Sjulian 341303612Sjulian processed = 1; 342303612Sjulian } 343303612Sjulian 344303612Sjulian pullup_len = l3_offset + hlen; 345303612Sjulian 346303612Sjulian /* We can not calculate a checksum fragmented packets */ 347303612Sjulian if (ip4->ip_off & htons(IP_MF|IP_OFFMASK)) { 348303612Sjulian m->m_pkthdr.csum_flags &= ~(CSUM_TCP|CSUM_UDP); 349303612Sjulian return (0); 350303612Sjulian } 351303612Sjulian 352303612Sjulian switch (ip4->ip_p) 353303612Sjulian { 354303612Sjulian case IPPROTO_TCP: 355303612Sjulian if (m->m_pkthdr.csum_flags & CSUM_TCP) { 356303612Sjulian struct tcphdr *th; 357303612Sjulian 358303612Sjulian PULLUP_CHECK(m, sizeof(struct tcphdr)); 359303612Sjulian th = (struct tcphdr *) mtodo(m, l3_offset + hlen); 360303612Sjulian 361303612Sjulian th->th_sum = in_pseudo(ip4->ip_src.s_addr, 362303612Sjulian ip4->ip_dst.s_addr, htons(ip4->ip_p + plen - hlen)); 363303612Sjulian 364303612Sjulian if ((priv->conf->csum_offload & CSUM_TCP) == 0) { 365303612Sjulian th->th_sum = in_cksum_skip(m, l3_offset + plen, l3_offset + hlen); 366303612Sjulian m->m_pkthdr.csum_flags &= ~CSUM_TCP; 367303612Sjulian } 368303612Sjulian 369303612Sjulian processed = 1; 370303612Sjulian } 371303612Sjulian 372303612Sjulian m->m_pkthdr.csum_flags &= ~CSUM_UDP; 373303612Sjulian break; 374303612Sjulian 375303612Sjulian case IPPROTO_UDP: 376303612Sjulian if (m->m_pkthdr.csum_flags & CSUM_UDP) { 377303612Sjulian struct udphdr *uh; 378303612Sjulian 379303612Sjulian PULLUP_CHECK(m, sizeof(struct udphdr)); 380303612Sjulian uh = (struct udphdr *) mtodo(m, l3_offset + hlen); 381303612Sjulian 382303612Sjulian uh->uh_sum = in_pseudo(ip4->ip_src.s_addr, 383303612Sjulian ip4->ip_dst.s_addr, htons(ip4->ip_p + plen - hlen)); 384303612Sjulian 385303612Sjulian if ((priv->conf->csum_offload & CSUM_UDP) == 0) { 386303612Sjulian uh->uh_sum = in_cksum_skip(m, 387303612Sjulian l3_offset + plen, l3_offset + hlen); 388303612Sjulian 389303612Sjulian if (uh->uh_sum == 0) 390303612Sjulian uh->uh_sum = 0xffff; 391303612Sjulian 392303612Sjulian m->m_pkthdr.csum_flags &= ~CSUM_UDP; 393303612Sjulian } 394303612Sjulian 395303612Sjulian processed = 1; 396303612Sjulian } 397303612Sjulian 398303612Sjulian m->m_pkthdr.csum_flags &= ~CSUM_TCP; 399303612Sjulian break; 400303612Sjulian 401303612Sjulian default: 402303612Sjulian m->m_pkthdr.csum_flags &= ~(CSUM_TCP|CSUM_UDP); 403303612Sjulian break; 404303612Sjulian } 405303612Sjulian 406303612Sjulian m->m_pkthdr.csum_flags &= ~NG_CHECKSUM_CSUM_IPV6; 407303612Sjulian 408303612Sjulian if (processed) 409303612Sjulian priv->stats.processed++; 410303612Sjulian 411303612Sjulian return (0); 412303612Sjulian} 413303612Sjulian#endif /* INET */ 414303612Sjulian 415303612Sjulian#ifdef INET6 416303612Sjulianstatic int 417303612Sjulianchecksum_ipv6(priv_p priv, struct mbuf *m, int l3_offset) 418303612Sjulian{ 419303612Sjulian struct ip6_hdr *ip6; 420303612Sjulian struct ip6_ext *ip6e = NULL; 421303612Sjulian int pullup_len; 422303612Sjulian int hlen, plen; 423303612Sjulian int nxt; 424303612Sjulian int processed = 0; 425303612Sjulian 426303612Sjulian pullup_len = l3_offset; 427303612Sjulian 428303612Sjulian PULLUP_CHECK(m, sizeof(struct ip6_hdr)); 429303612Sjulian ip6 = (struct ip6_hdr *) mtodo(m, l3_offset); 430303612Sjulian 431303612Sjulian if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) 432303612Sjulian return (EOPNOTSUPP); 433303612Sjulian 434303612Sjulian hlen = sizeof(struct ip6_hdr); 435303612Sjulian plen = ntohs(ip6->ip6_plen) + hlen; 436303612Sjulian 437303612Sjulian if (m->m_pkthdr.len < l3_offset + plen) 438303612Sjulian return (EINVAL); 439303612Sjulian 440303612Sjulian nxt = ip6->ip6_nxt; 441303612Sjulian 442303612Sjulian for (;;) { 443303612Sjulian switch (nxt) 444303612Sjulian { 445303612Sjulian case IPPROTO_DSTOPTS: 446303612Sjulian case IPPROTO_HOPOPTS: 447303612Sjulian case IPPROTO_ROUTING: 448303612Sjulian PULLUP_CHECK(m, sizeof(struct ip6_ext)); 449303612Sjulian ip6e = (struct ip6_ext *) mtodo(m, l3_offset + hlen); 450303612Sjulian nxt = ip6e->ip6e_nxt; 451303612Sjulian hlen += (ip6e->ip6e_len + 1) << 3; 452303612Sjulian pullup_len = l3_offset + hlen; 453303612Sjulian break; 454303612Sjulian 455303612Sjulian case IPPROTO_AH: 456303612Sjulian PULLUP_CHECK(m, sizeof(struct ip6_ext)); 457303612Sjulian ip6e = (struct ip6_ext *) mtodo(m, l3_offset + hlen); 458303612Sjulian nxt = ip6e->ip6e_nxt; 459303612Sjulian hlen += (ip6e->ip6e_len + 2) << 2; 460303612Sjulian pullup_len = l3_offset + hlen; 461303612Sjulian break; 462303612Sjulian 463303612Sjulian case IPPROTO_FRAGMENT: 464303612Sjulian /* We can not calculate a checksum fragmented packets */ 465303612Sjulian m->m_pkthdr.csum_flags &= ~(CSUM_TCP_IPV6|CSUM_UDP_IPV6); 466303612Sjulian return (0); 467303612Sjulian 468303612Sjulian default: 469303612Sjulian goto loopend; 470303612Sjulian } 471303612Sjulian 472303612Sjulian if (nxt == 0) 473303612Sjulian return (EINVAL); 474303612Sjulian } 475303612Sjulian 476303612Sjulianloopend: 477303612Sjulian 478303612Sjulian switch (nxt) 479303612Sjulian { 480303612Sjulian case IPPROTO_TCP: 481303612Sjulian if (m->m_pkthdr.csum_flags & CSUM_TCP_IPV6) { 482303612Sjulian struct tcphdr *th; 483303612Sjulian 484303612Sjulian PULLUP_CHECK(m, sizeof(struct tcphdr)); 485303612Sjulian th = (struct tcphdr *) mtodo(m, l3_offset + hlen); 486303612Sjulian 487303612Sjulian th->th_sum = in6_cksum_pseudo(ip6, plen - hlen, nxt, 0); 488303612Sjulian 489303612Sjulian if ((priv->conf->csum_offload & CSUM_TCP_IPV6) == 0) { 490303612Sjulian th->th_sum = in_cksum_skip(m, l3_offset + plen, l3_offset + hlen); 491303612Sjulian m->m_pkthdr.csum_flags &= ~CSUM_TCP_IPV6; 492303612Sjulian } 493303612Sjulian 494303612Sjulian processed = 1; 495303612Sjulian } 496303612Sjulian 497303612Sjulian m->m_pkthdr.csum_flags &= ~CSUM_UDP_IPV6; 498303612Sjulian break; 499303612Sjulian 500303612Sjulian case IPPROTO_UDP: 501303612Sjulian if (m->m_pkthdr.csum_flags & CSUM_UDP_IPV6) { 502303612Sjulian struct udphdr *uh; 503303612Sjulian 504303612Sjulian PULLUP_CHECK(m, sizeof(struct udphdr)); 505303612Sjulian uh = (struct udphdr *) mtodo(m, l3_offset + hlen); 506303612Sjulian 507303612Sjulian uh->uh_sum = in6_cksum_pseudo(ip6, plen - hlen, nxt, 0); 508303612Sjulian 509303612Sjulian if ((priv->conf->csum_offload & CSUM_UDP_IPV6) == 0) { 510303612Sjulian uh->uh_sum = in_cksum_skip(m, 511303612Sjulian l3_offset + plen, l3_offset + hlen); 512303612Sjulian 513303612Sjulian if (uh->uh_sum == 0) 514303612Sjulian uh->uh_sum = 0xffff; 515303612Sjulian 516303612Sjulian m->m_pkthdr.csum_flags &= ~CSUM_UDP_IPV6; 517303612Sjulian } 518303612Sjulian 519303612Sjulian processed = 1; 520303612Sjulian } 521303612Sjulian 522303612Sjulian m->m_pkthdr.csum_flags &= ~CSUM_TCP_IPV6; 523303612Sjulian break; 524303612Sjulian 525303612Sjulian default: 526303612Sjulian m->m_pkthdr.csum_flags &= ~(CSUM_TCP_IPV6|CSUM_UDP_IPV6); 527303612Sjulian break; 528303612Sjulian } 529303612Sjulian 530303612Sjulian m->m_pkthdr.csum_flags &= ~NG_CHECKSUM_CSUM_IPV4; 531303612Sjulian 532303612Sjulian if (processed) 533303612Sjulian priv->stats.processed++; 534303612Sjulian 535303612Sjulian return (0); 536303612Sjulian} 537303612Sjulian#endif /* INET6 */ 538303612Sjulian 539303612Sjulian#undef PULLUP_CHECK 540303612Sjulian 541303612Sjulianstatic int 542303612Sjulianng_checksum_rcvdata(hook_p hook, item_p item) 543303612Sjulian{ 544303612Sjulian const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 545303612Sjulian struct mbuf *m; 546303612Sjulian hook_p out; 547303612Sjulian int error = 0; 548303612Sjulian 549303612Sjulian priv->stats.received++; 550303612Sjulian 551303612Sjulian NGI_GET_M(item, m); 552303612Sjulian 553303612Sjulian#define PULLUP_CHECK(mbuf, length) do { \ 554303612Sjulian pullup_len += length; \ 555303612Sjulian if (((mbuf)->m_pkthdr.len < pullup_len) || \ 556303612Sjulian (pullup_len > MHLEN)) { \ 557303612Sjulian error = EINVAL; \ 558303612Sjulian goto bypass; \ 559303612Sjulian } \ 560303612Sjulian if ((mbuf)->m_len < pullup_len && \ 561303612Sjulian (((mbuf) = m_pullup((mbuf), pullup_len)) == NULL)) { \ 562303612Sjulian error = ENOBUFS; \ 563303612Sjulian goto drop; \ 564303612Sjulian } \ 565303612Sjulian} while (0) 566303612Sjulian 567303612Sjulian if (!(priv->conf && hook == priv->in && m && (m->m_flags & M_PKTHDR))) 568303612Sjulian goto bypass; 569303612Sjulian 570303612Sjulian m->m_pkthdr.csum_flags |= priv->conf->csum_flags; 571303612Sjulian 572303612Sjulian if (m->m_pkthdr.csum_flags & (NG_CHECKSUM_CSUM_IPV4|NG_CHECKSUM_CSUM_IPV6)) 573303612Sjulian { 574303612Sjulian struct ether_header *eh; 575303612Sjulian struct ng_checksum_vlan_header *vh; 576303612Sjulian int pullup_len = 0; 577303612Sjulian uint16_t etype; 578303612Sjulian 579303612Sjulian m = m_unshare(m, M_NOWAIT); 580303612Sjulian 581303612Sjulian if (m == NULL) 582303612Sjulian ERROUT(ENOMEM); 583303612Sjulian 584303612Sjulian switch (priv->dlt) 585303612Sjulian { 586303612Sjulian case DLT_EN10MB: 587303612Sjulian PULLUP_CHECK(m, sizeof(struct ether_header)); 588303612Sjulian eh = mtod(m, struct ether_header *); 589303612Sjulian etype = ntohs(eh->ether_type); 590303612Sjulian 591303612Sjulian for (;;) { /* QinQ support */ 592303612Sjulian switch (etype) 593303612Sjulian { 594303612Sjulian case 0x8100: 595303612Sjulian case 0x88A8: 596303612Sjulian case 0x9100: 597303612Sjulian PULLUP_CHECK(m, sizeof(struct ng_checksum_vlan_header)); 598303612Sjulian vh = (struct ng_checksum_vlan_header *) mtodo(m, 599303612Sjulian pullup_len - sizeof(struct ng_checksum_vlan_header)); 600303612Sjulian etype = ntohs(vh->etype); 601303612Sjulian break; 602303612Sjulian 603303612Sjulian default: 604303612Sjulian goto loopend; 605303612Sjulian } 606303612Sjulian } 607303612Sjulianloopend: 608303612Sjulian#ifdef INET 609303612Sjulian if (etype == ETHERTYPE_IP && 610303612Sjulian (m->m_pkthdr.csum_flags & NG_CHECKSUM_CSUM_IPV4)) { 611303612Sjulian error = checksum_ipv4(priv, m, pullup_len); 612303612Sjulian if (error == ENOBUFS) 613303612Sjulian goto drop; 614303612Sjulian } else 615303612Sjulian#endif 616303612Sjulian#ifdef INET6 617303612Sjulian if (etype == ETHERTYPE_IPV6 && 618303612Sjulian (m->m_pkthdr.csum_flags & NG_CHECKSUM_CSUM_IPV6)) { 619303612Sjulian error = checksum_ipv6(priv, m, pullup_len); 620303612Sjulian if (error == ENOBUFS) 621303612Sjulian goto drop; 622303612Sjulian } else 623303612Sjulian#endif 624303612Sjulian { 625303612Sjulian m->m_pkthdr.csum_flags &= 626303612Sjulian ~(NG_CHECKSUM_CSUM_IPV4|NG_CHECKSUM_CSUM_IPV6); 627303612Sjulian } 628303612Sjulian 629303612Sjulian break; 630303612Sjulian 631303612Sjulian case DLT_RAW: 632303612Sjulian#ifdef INET 633303612Sjulian if (m->m_pkthdr.csum_flags & NG_CHECKSUM_CSUM_IPV4) 634303612Sjulian { 635303612Sjulian error = checksum_ipv4(priv, m, pullup_len); 636303612Sjulian 637303612Sjulian if (error == 0) 638303612Sjulian goto bypass; 639303612Sjulian else if (error == ENOBUFS) 640303612Sjulian goto drop; 641303612Sjulian } 642303612Sjulian#endif 643303612Sjulian#ifdef INET6 644303612Sjulian if (m->m_pkthdr.csum_flags & NG_CHECKSUM_CSUM_IPV6) 645303612Sjulian { 646303612Sjulian error = checksum_ipv6(priv, m, pullup_len); 647303612Sjulian 648303612Sjulian if (error == 0) 649303612Sjulian goto bypass; 650303612Sjulian else if (error == ENOBUFS) 651303612Sjulian goto drop; 652303612Sjulian } 653303612Sjulian#endif 654303612Sjulian if (error) 655303612Sjulian m->m_pkthdr.csum_flags &= 656303612Sjulian ~(NG_CHECKSUM_CSUM_IPV4|NG_CHECKSUM_CSUM_IPV6); 657303612Sjulian 658303612Sjulian break; 659303612Sjulian 660303612Sjulian default: 661303612Sjulian ERROUT(EINVAL); 662303612Sjulian } 663303612Sjulian } 664303612Sjulian 665303612Sjulian#undef PULLUP_CHECK 666303612Sjulian 667303612Sjulianbypass: 668303612Sjulian out = NULL; 669303612Sjulian 670303612Sjulian if (hook == priv->in) { 671303612Sjulian /* return frames on 'in' hook if 'out' not connected */ 672303612Sjulian out = priv->out ? priv->out : priv->in; 673303612Sjulian } else if (hook == priv->out && priv->in) { 674303612Sjulian /* pass frames on 'out' hook if 'in' connected */ 675303612Sjulian out = priv->in; 676303612Sjulian } 677303612Sjulian 678303612Sjulian if (out == NULL) 679303612Sjulian ERROUT(0); 680303612Sjulian 681303612Sjulian NG_FWD_NEW_DATA(error, item, out, m); 682303612Sjulian 683303612Sjulian return (error); 684303612Sjulian 685303612Sjuliandone: 686303612Sjuliandrop: 687303612Sjulian NG_FREE_ITEM(item); 688303612Sjulian NG_FREE_M(m); 689303612Sjulian 690303612Sjulian priv->stats.dropped++; 691303612Sjulian 692303612Sjulian return (error); 693303612Sjulian} 694303612Sjulian 695303612Sjulianstatic int 696303612Sjulianng_checksum_shutdown(node_p node) 697303612Sjulian{ 698303612Sjulian const priv_p priv = NG_NODE_PRIVATE(node); 699303612Sjulian 700303612Sjulian NG_NODE_SET_PRIVATE(node, NULL); 701303612Sjulian NG_NODE_UNREF(node); 702303612Sjulian 703303612Sjulian if (priv->conf) 704303612Sjulian free(priv->conf, M_NETGRAPH); 705303612Sjulian 706303612Sjulian free(priv, M_NETGRAPH); 707303612Sjulian 708303612Sjulian return (0); 709303612Sjulian} 710303612Sjulian 711303612Sjulianstatic int 712303612Sjulianng_checksum_disconnect(hook_p hook) 713303612Sjulian{ 714303612Sjulian priv_p priv; 715303612Sjulian 716303612Sjulian priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 717303612Sjulian 718303612Sjulian if (hook == priv->in) 719303612Sjulian priv->in = NULL; 720303612Sjulian 721303612Sjulian if (hook == priv->out) 722303612Sjulian priv->out = NULL; 723303612Sjulian 724303612Sjulian if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 && 725303612Sjulian NG_NODE_IS_VALID(NG_HOOK_NODE(hook))) /* already shutting down? */ 726303612Sjulian ng_rmnode_self(NG_HOOK_NODE(hook)); 727303612Sjulian 728303612Sjulian return (0); 729303612Sjulian} 730