/* Protect conntrack agaist broken packets. Code taken from ipt_unclean.c. */ static bool tcp_error(const struct tcphdr *th, struct sk_buff *skb, unsigned int dataoff, const struct nf_hook_state *state) { unsigned int tcplen = skb->len - dataoff; u8 tcpflags; /* Not whole TCP header or malformed packet */ if (th->doff*4 < sizeof(struct tcphdr) || tcplen < th->doff*4) { tcp_error_log(skb, state, "truncated packet"); return true; } /* Checksum invalid? Ignore. * We skip checking packets on the outgoing path * because the checksum is assumed to be correct. */ /* FIXME: Source route IP option packets --RR */ if (state->net->ct.sysctl_checksum && state->hook == NF_INET_PRE_ROUTING && nf_checksum(skb, state->hook, dataoff, IPPROTO_TCP, state->pf)) { tcp_error_log(skb, state, "bad checksum"); return true; } /* Check TCP flags. */ tcpflags = (tcp_flag_byte(th) & ~(TCPHDR_ECE|TCPHDR_CWR|TCPHDR_PSH)); if (!tcp_valid_flags[tcpflags]) { tcp_error_log(skb, state, "invalid tcp flag combination"); return true; } return false; }
/* Protect conntrack agaist broken packets. Code taken from ipt_unclean.c. */ static int tcp_error(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info *ctinfo, u_int8_t pf, unsigned int hooknum) { const struct tcphdr *th; struct tcphdr _tcph; unsigned int tcplen = skb->len - dataoff; u_int8_t tcpflags; /* Smaller that minimal TCP header? */ th = skb_header_pointer(skb, dataoff, sizeof(_tcph), &_tcph); if (th == NULL) { if (LOG_INVALID(net, IPPROTO_TCP)) nf_log_packet(pf, 0, skb, NULL, NULL, NULL, "nf_ct_tcp: short packet "); return -NF_ACCEPT; } /* Not whole TCP header or malformed packet */ if (th->doff*4 < sizeof(struct tcphdr) || tcplen < th->doff*4) { if (LOG_INVALID(net, IPPROTO_TCP)) nf_log_packet(pf, 0, skb, NULL, NULL, NULL, "nf_ct_tcp: truncated/malformed packet "); return -NF_ACCEPT; } /* Checksum invalid? Ignore. * We skip checking packets on the outgoing path * because the checksum is assumed to be correct. */ /* FIXME: Source route IP option packets --RR */ if (net->ct.sysctl_checksum && hooknum == NF_INET_PRE_ROUTING && nf_checksum(skb, hooknum, dataoff, IPPROTO_TCP, pf)) { if (LOG_INVALID(net, IPPROTO_TCP)) nf_log_packet(pf, 0, skb, NULL, NULL, NULL, "nf_ct_tcp: bad TCP checksum "); return -NF_ACCEPT; } /* Check TCP flags. */ tcpflags = (tcp_flag_byte(th) & ~(TCPHDR_ECE|TCPHDR_CWR|TCPHDR_PSH)); if (!tcp_valid_flags[tcpflags]) { if (LOG_INVALID(net, IPPROTO_TCP)) nf_log_packet(pf, 0, skb, NULL, NULL, NULL, "nf_ct_tcp: invalid TCP flag combination "); return -NF_ACCEPT; } return NF_ACCEPT; }
static int tcp_error(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info *ctinfo, u_int8_t pf, unsigned int hooknum) { const struct tcphdr *th; struct tcphdr _tcph; unsigned int tcplen = skb->len - dataoff; u_int8_t tcpflags; th = skb_header_pointer(skb, dataoff, sizeof(_tcph), &_tcph); if (th == NULL) { if (LOG_INVALID(net, IPPROTO_TCP)) nf_log_packet(pf, 0, skb, NULL, NULL, NULL, "nf_ct_tcp: short packet "); return -NF_ACCEPT; } if (th->doff*4 < sizeof(struct tcphdr) || tcplen < th->doff*4) { if (LOG_INVALID(net, IPPROTO_TCP)) nf_log_packet(pf, 0, skb, NULL, NULL, NULL, "nf_ct_tcp: truncated/malformed packet "); return -NF_ACCEPT; } if (net->ct.sysctl_checksum && hooknum == NF_INET_PRE_ROUTING && nf_checksum(skb, hooknum, dataoff, IPPROTO_TCP, pf)) { if (LOG_INVALID(net, IPPROTO_TCP)) nf_log_packet(pf, 0, skb, NULL, NULL, NULL, "nf_ct_tcp: bad TCP checksum "); return -NF_ACCEPT; } tcpflags = (tcp_flag_byte(th) & ~(TCPHDR_ECE|TCPHDR_CWR|TCPHDR_PSH)); if (!tcp_valid_flags[tcpflags]) { if (LOG_INVALID(net, IPPROTO_TCP)) nf_log_packet(pf, 0, skb, NULL, NULL, NULL, "nf_ct_tcp: invalid TCP flag combination "); return -NF_ACCEPT; } return NF_ACCEPT; }
/** * tso_start_new_packet - generate a new header and prepare for the new packet * @tx_queue: Efx TX queue * @skb: Socket buffer * @st: TSO state * @is_first: true if this is the first packet * * Generate a new header and prepare for the new packet. Return 0 on * success, or -%ENOMEM if failed to alloc header. */ static int tso_start_new_packet(struct efx_tx_queue *tx_queue, const struct sk_buff *skb, struct tso_state *st, bool is_first) { struct efx_tx_buffer *buffer = efx_tx_queue_get_insert_buffer(tx_queue); bool is_last = st->out_len <= skb_shinfo(skb)->gso_size; u8 tcp_flags_mask; if (!is_last) { st->packet_space = skb_shinfo(skb)->gso_size; tcp_flags_mask = TCPHDR_FIN | TCPHDR_PSH; } else { st->packet_space = st->out_len; tcp_flags_mask = 0; } if (!is_first) tcp_flags_mask |= TCPHDR_CWR; /* Congestion control */ if (!st->header_unmap_len) { /* Allocate and insert a DMA-mapped header buffer. */ struct tcphdr *tsoh_th; unsigned int ip_length; u8 *header; int rc; header = efx_tsoh_get_buffer(tx_queue, buffer, st->header_len); if (!header) return -ENOMEM; tsoh_th = (struct tcphdr *)(header + st->tcp_off); /* Copy and update the headers. */ memcpy(header, skb->data, st->header_len); tsoh_th->seq = htonl(st->seqnum); tcp_flag_byte(tsoh_th) &= ~tcp_flags_mask; ip_length = st->ip_base_len + st->packet_space; if (st->protocol == htons(ETH_P_IP)) { struct iphdr *tsoh_iph = (struct iphdr *)(header + st->ip_off); tsoh_iph->tot_len = htons(ip_length); tsoh_iph->id = htons(st->ipv4_id); } else { struct ipv6hdr *tsoh_iph = (struct ipv6hdr *)(header + st->ip_off); tsoh_iph->payload_len = htons(ip_length); } rc = efx_tso_put_header(tx_queue, buffer, header); if (unlikely(rc)) return rc; } else { /* Send the original headers with a TSO option descriptor * in front */ u8 tcp_flags = tcp_flag_byte(tcp_hdr(skb)) & ~tcp_flags_mask; buffer->flags = EFX_TX_BUF_OPTION; buffer->len = 0; buffer->unmap_len = 0; EFX_POPULATE_QWORD_5(buffer->option, ESF_DZ_TX_DESC_IS_OPT, 1, ESF_DZ_TX_OPTION_TYPE, ESE_DZ_TX_OPTION_DESC_TSO, ESF_DZ_TX_TSO_TCP_FLAGS, tcp_flags, ESF_DZ_TX_TSO_IP_ID, st->ipv4_id, ESF_DZ_TX_TSO_TCP_SEQNO, st->seqnum); ++tx_queue->insert_count; /* We mapped the headers in tso_start(). Unmap them * when the last segment is completed. */ buffer = efx_tx_queue_get_insert_buffer(tx_queue); buffer->dma_addr = st->header_dma_addr; buffer->len = st->header_len; if (is_last) { buffer->flags = EFX_TX_BUF_CONT | EFX_TX_BUF_MAP_SINGLE; buffer->unmap_len = st->header_unmap_len; buffer->dma_offset = 0; /* Ensure we only unmap them once in case of a * later DMA mapping error and rollback */ st->header_unmap_len = 0; } else { buffer->flags = EFX_TX_BUF_CONT; buffer->unmap_len = 0; } ++tx_queue->insert_count; } st->seqnum += skb_shinfo(skb)->gso_size; /* Linux leaves suitable gaps in the IP ID space for us to fill. */ ++st->ipv4_id; return 0; }