Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

3 Commits   0 Branches   0 Tags
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   1) // SPDX-License-Identifier: GPL-2.0-or-later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   3)  *	GRE over IPv4 demultiplexer driver
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   4)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   5)  *	Authors: Dmitry Kozlov (xeb@mail.ru)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   6)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   7) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   8) #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   9) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  10) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  11) #include <linux/if.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  12) #include <linux/icmp.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  13) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  14) #include <linux/kmod.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  15) #include <linux/skbuff.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  16) #include <linux/in.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  17) #include <linux/ip.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  18) #include <linux/netdevice.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  19) #include <linux/if_tunnel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  20) #include <linux/spinlock.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  21) #include <net/protocol.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  22) #include <net/gre.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  23) #include <net/erspan.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  24) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  25) #include <net/icmp.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  26) #include <net/route.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  27) #include <net/xfrm.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  28) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  29) static const struct gre_protocol __rcu *gre_proto[GREPROTO_MAX] __read_mostly;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  30) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  31) int gre_add_protocol(const struct gre_protocol *proto, u8 version)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  32) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  33) 	if (version >= GREPROTO_MAX)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  34) 		return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  35) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  36) 	return (cmpxchg((const struct gre_protocol **)&gre_proto[version], NULL, proto) == NULL) ?
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  37) 		0 : -EBUSY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  38) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  39) EXPORT_SYMBOL_GPL(gre_add_protocol);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  40) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  41) int gre_del_protocol(const struct gre_protocol *proto, u8 version)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  42) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  43) 	int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  44) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  45) 	if (version >= GREPROTO_MAX)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  46) 		return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  47) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  48) 	ret = (cmpxchg((const struct gre_protocol **)&gre_proto[version], proto, NULL) == proto) ?
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  49) 		0 : -EBUSY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  50) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  51) 	if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  52) 		return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  53) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  54) 	synchronize_rcu();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  55) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  56) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  57) EXPORT_SYMBOL_GPL(gre_del_protocol);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  58) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  59) /* Fills in tpi and returns header length to be pulled.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  60)  * Note that caller must use pskb_may_pull() before pulling GRE header.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  61)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  62) int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  63) 		     bool *csum_err, __be16 proto, int nhs)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  64) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  65) 	const struct gre_base_hdr *greh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  66) 	__be32 *options;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  67) 	int hdr_len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  68) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  69) 	if (unlikely(!pskb_may_pull(skb, nhs + sizeof(struct gre_base_hdr))))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  70) 		return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  71) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  72) 	greh = (struct gre_base_hdr *)(skb->data + nhs);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  73) 	if (unlikely(greh->flags & (GRE_VERSION | GRE_ROUTING)))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  74) 		return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  75) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  76) 	tpi->flags = gre_flags_to_tnl_flags(greh->flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  77) 	hdr_len = gre_calc_hlen(tpi->flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  78) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  79) 	if (!pskb_may_pull(skb, nhs + hdr_len))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  80) 		return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  81) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  82) 	greh = (struct gre_base_hdr *)(skb->data + nhs);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  83) 	tpi->proto = greh->protocol;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  84) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  85) 	options = (__be32 *)(greh + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  86) 	if (greh->flags & GRE_CSUM) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  87) 		if (!skb_checksum_simple_validate(skb)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  88) 			skb_checksum_try_convert(skb, IPPROTO_GRE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  89) 						 null_compute_pseudo);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  90) 		} else if (csum_err) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  91) 			*csum_err = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  92) 			return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  93) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  94) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  95) 		options++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  96) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  97) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  98) 	if (greh->flags & GRE_KEY) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  99) 		tpi->key = *options;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) 		options++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) 	} else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) 		tpi->key = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) 	if (unlikely(greh->flags & GRE_SEQ)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) 		tpi->seq = *options;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) 		options++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) 	} else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) 		tpi->seq = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) 	/* WCCP version 1 and 2 protocol decoding.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) 	 * - Change protocol to IPv4/IPv6
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) 	 * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) 	if (greh->flags == 0 && tpi->proto == htons(ETH_P_WCCP)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) 		u8 _val, *val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) 		val = skb_header_pointer(skb, nhs + hdr_len,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) 					 sizeof(_val), &_val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) 		if (!val)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) 			return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) 		tpi->proto = proto;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) 		if ((*val & 0xF0) != 0x40)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) 			hdr_len += 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) 	tpi->hdr_len = hdr_len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) 	/* ERSPAN ver 1 and 2 protocol sets GRE key field
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) 	 * to 0 and sets the configured key in the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) 	 * inner erspan header field
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) 	if ((greh->protocol == htons(ETH_P_ERSPAN) && hdr_len != 4) ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) 	    greh->protocol == htons(ETH_P_ERSPAN2)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) 		struct erspan_base_hdr *ershdr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) 		if (!pskb_may_pull(skb, nhs + hdr_len + sizeof(*ershdr)))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) 			return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) 		ershdr = (struct erspan_base_hdr *)(skb->data + nhs + hdr_len);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) 		tpi->key = cpu_to_be32(get_session_id(ershdr));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) 	return hdr_len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) EXPORT_SYMBOL(gre_parse_header);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) static int gre_rcv(struct sk_buff *skb)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) 	const struct gre_protocol *proto;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) 	u8 ver;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) 	int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) 	if (!pskb_may_pull(skb, 12))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) 		goto drop;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) 	ver = skb->data[1]&0x7f;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) 	if (ver >= GREPROTO_MAX)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) 		goto drop;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) 	rcu_read_lock();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) 	proto = rcu_dereference(gre_proto[ver]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) 	if (!proto || !proto->handler)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) 		goto drop_unlock;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) 	ret = proto->handler(skb);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) 	rcu_read_unlock();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) 	return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) drop_unlock:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) 	rcu_read_unlock();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) drop:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) 	kfree_skb(skb);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) 	return NET_RX_DROP;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) static int gre_err(struct sk_buff *skb, u32 info)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) 	const struct gre_protocol *proto;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) 	const struct iphdr *iph = (const struct iphdr *)skb->data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) 	u8 ver = skb->data[(iph->ihl<<2) + 1]&0x7f;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) 	int err = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) 	if (ver >= GREPROTO_MAX)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) 		return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) 	rcu_read_lock();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) 	proto = rcu_dereference(gre_proto[ver]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) 	if (proto && proto->err_handler)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) 		proto->err_handler(skb, info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) 	else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) 		err = -EPROTONOSUPPORT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) 	rcu_read_unlock();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) 	return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) static const struct net_protocol net_gre_protocol = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) 	.handler     = gre_rcv,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) 	.err_handler = gre_err,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) 	.netns_ok    = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) static int __init gre_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) 	pr_info("GRE over IPv4 demultiplexor driver\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) 	if (inet_add_protocol(&net_gre_protocol, IPPROTO_GRE) < 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) 		pr_err("can't add protocol\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) 		return -EAGAIN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) static void __exit gre_exit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) 	inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) module_init(gre_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) module_exit(gre_exit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) MODULE_DESCRIPTION("GRE over IPv4 demultiplexer driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) MODULE_AUTHOR("D. Kozlov (xeb@mail.ru)");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) MODULE_LICENSE("GPL");