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