18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Definitions for the UDP-Lite (RFC 3828) code. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci#ifndef _UDPLITE_H 68c2ecf20Sopenharmony_ci#define _UDPLITE_H 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <net/ip6_checksum.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci/* UDP-Lite socket options */ 118c2ecf20Sopenharmony_ci#define UDPLITE_SEND_CSCOV 10 /* sender partial coverage (as sent) */ 128c2ecf20Sopenharmony_ci#define UDPLITE_RECV_CSCOV 11 /* receiver partial coverage (threshold ) */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ciextern struct proto udplite_prot; 158c2ecf20Sopenharmony_ciextern struct udp_table udplite_table; 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* 188c2ecf20Sopenharmony_ci * Checksum computation is all in software, hence simpler getfrag. 198c2ecf20Sopenharmony_ci */ 208c2ecf20Sopenharmony_cistatic __inline__ int udplite_getfrag(void *from, char *to, int offset, 218c2ecf20Sopenharmony_ci int len, int odd, struct sk_buff *skb) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci struct msghdr *msg = from; 248c2ecf20Sopenharmony_ci return copy_from_iter_full(to, len, &msg->msg_iter) ? 0 : -EFAULT; 258c2ecf20Sopenharmony_ci} 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* 288c2ecf20Sopenharmony_ci * Checksumming routines 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_cistatic inline int udplite_checksum_init(struct sk_buff *skb, struct udphdr *uh) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci u16 cscov; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci /* In UDPv4 a zero checksum means that the transmitter generated no 358c2ecf20Sopenharmony_ci * checksum. UDP-Lite (like IPv6) mandates checksums, hence packets 368c2ecf20Sopenharmony_ci * with a zero checksum field are illegal. */ 378c2ecf20Sopenharmony_ci if (uh->check == 0) { 388c2ecf20Sopenharmony_ci net_dbg_ratelimited("UDPLite: zeroed checksum field\n"); 398c2ecf20Sopenharmony_ci return 1; 408c2ecf20Sopenharmony_ci } 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci cscov = ntohs(uh->len); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci if (cscov == 0) /* Indicates that full coverage is required. */ 458c2ecf20Sopenharmony_ci ; 468c2ecf20Sopenharmony_ci else if (cscov < 8 || cscov > skb->len) { 478c2ecf20Sopenharmony_ci /* 488c2ecf20Sopenharmony_ci * Coverage length violates RFC 3828: log and discard silently. 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_ci net_dbg_ratelimited("UDPLite: bad csum coverage %d/%d\n", 518c2ecf20Sopenharmony_ci cscov, skb->len); 528c2ecf20Sopenharmony_ci return 1; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci } else if (cscov < skb->len) { 558c2ecf20Sopenharmony_ci UDP_SKB_CB(skb)->partial_cov = 1; 568c2ecf20Sopenharmony_ci UDP_SKB_CB(skb)->cscov = cscov; 578c2ecf20Sopenharmony_ci if (skb->ip_summed == CHECKSUM_COMPLETE) 588c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 598c2ecf20Sopenharmony_ci skb->csum_valid = 0; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return 0; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* Slow-path computation of checksum. Socket is locked. */ 668c2ecf20Sopenharmony_cistatic inline __wsum udplite_csum_outgoing(struct sock *sk, struct sk_buff *skb) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci const struct udp_sock *up = udp_sk(skb->sk); 698c2ecf20Sopenharmony_ci int cscov = up->len; 708c2ecf20Sopenharmony_ci __wsum csum = 0; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (up->pcflag & UDPLITE_SEND_CC) { 738c2ecf20Sopenharmony_ci /* 748c2ecf20Sopenharmony_ci * Sender has set `partial coverage' option on UDP-Lite socket. 758c2ecf20Sopenharmony_ci * The special case "up->pcslen == 0" signifies full coverage. 768c2ecf20Sopenharmony_ci */ 778c2ecf20Sopenharmony_ci if (up->pcslen < up->len) { 788c2ecf20Sopenharmony_ci if (0 < up->pcslen) 798c2ecf20Sopenharmony_ci cscov = up->pcslen; 808c2ecf20Sopenharmony_ci udp_hdr(skb)->len = htons(up->pcslen); 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci /* 838c2ecf20Sopenharmony_ci * NOTE: Causes for the error case `up->pcslen > up->len': 848c2ecf20Sopenharmony_ci * (i) Application error (will not be penalized). 858c2ecf20Sopenharmony_ci * (ii) Payload too big for send buffer: data is split 868c2ecf20Sopenharmony_ci * into several packets, each with its own header. 878c2ecf20Sopenharmony_ci * In this case (e.g. last segment), coverage may 888c2ecf20Sopenharmony_ci * exceed packet length. 898c2ecf20Sopenharmony_ci * Since packets with coverage length > packet length are 908c2ecf20Sopenharmony_ci * illegal, we fall back to the defaults here. 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; /* no HW support for checksumming */ 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci skb_queue_walk(&sk->sk_write_queue, skb) { 978c2ecf20Sopenharmony_ci const int off = skb_transport_offset(skb); 988c2ecf20Sopenharmony_ci const int len = skb->len - off; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci csum = skb_checksum(skb, off, (cscov > len)? len : cscov, csum); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if ((cscov -= len) <= 0) 1038c2ecf20Sopenharmony_ci break; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci return csum; 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/* Fast-path computation of checksum. Socket may not be locked. */ 1098c2ecf20Sopenharmony_cistatic inline __wsum udplite_csum(struct sk_buff *skb) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci const struct udp_sock *up = udp_sk(skb->sk); 1128c2ecf20Sopenharmony_ci const int off = skb_transport_offset(skb); 1138c2ecf20Sopenharmony_ci int len = skb->len - off; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if ((up->pcflag & UDPLITE_SEND_CC) && up->pcslen < len) { 1168c2ecf20Sopenharmony_ci if (0 < up->pcslen) 1178c2ecf20Sopenharmony_ci len = up->pcslen; 1188c2ecf20Sopenharmony_ci udp_hdr(skb)->len = htons(up->pcslen); 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; /* no HW support for checksumming */ 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return skb_checksum(skb, off, len, 0); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_civoid udplite4_register(void); 1268c2ecf20Sopenharmony_ciint udplite_get_port(struct sock *sk, unsigned short snum, 1278c2ecf20Sopenharmony_ci int (*scmp)(const struct sock *, const struct sock *)); 1288c2ecf20Sopenharmony_ci#endif /* _UDPLITE_H */ 129