162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * INET		An implementation of the TCP/IP protocol suite for the LINUX
462306a36Sopenharmony_ci *		operating system.  INET is implemented using the  BSD Socket
562306a36Sopenharmony_ci *		interface as the means of communication with the user level.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *		INET protocol dispatch tables.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Authors:	Ross Biro
1062306a36Sopenharmony_ci *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * Fixes:
1362306a36Sopenharmony_ci *		Alan Cox	: Ahah! udp icmp errors don't work because
1462306a36Sopenharmony_ci *				  udp_err is never called!
1562306a36Sopenharmony_ci *		Alan Cox	: Added new fields for init and ready for
1662306a36Sopenharmony_ci *				  proper fragmentation (_NO_ 4K limits!)
1762306a36Sopenharmony_ci *		Richard Colella	: Hang on hash collision
1862306a36Sopenharmony_ci *		Vince Laviano	: Modified inet_del_protocol() to correctly
1962306a36Sopenharmony_ci *				  maintain copy bit.
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_ci#include <linux/cache.h>
2262306a36Sopenharmony_ci#include <linux/module.h>
2362306a36Sopenharmony_ci#include <linux/netdevice.h>
2462306a36Sopenharmony_ci#include <linux/spinlock.h>
2562306a36Sopenharmony_ci#include <net/protocol.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistruct net_protocol __rcu *inet_protos[MAX_INET_PROTOS] __read_mostly;
2862306a36Sopenharmony_ciEXPORT_SYMBOL(inet_protos);
2962306a36Sopenharmony_ciconst struct net_offload __rcu *inet_offloads[MAX_INET_PROTOS] __read_mostly;
3062306a36Sopenharmony_ciEXPORT_SYMBOL(inet_offloads);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ciint inet_add_protocol(const struct net_protocol *prot, unsigned char protocol)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	return !cmpxchg((const struct net_protocol **)&inet_protos[protocol],
3562306a36Sopenharmony_ci			NULL, prot) ? 0 : -1;
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ciEXPORT_SYMBOL(inet_add_protocol);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ciint inet_add_offload(const struct net_offload *prot, unsigned char protocol)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	return !cmpxchg((const struct net_offload **)&inet_offloads[protocol],
4262306a36Sopenharmony_ci			NULL, prot) ? 0 : -1;
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ciEXPORT_SYMBOL(inet_add_offload);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ciint inet_del_protocol(const struct net_protocol *prot, unsigned char protocol)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	int ret;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	ret = (cmpxchg((const struct net_protocol **)&inet_protos[protocol],
5162306a36Sopenharmony_ci		       prot, NULL) == prot) ? 0 : -1;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	synchronize_net();
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	return ret;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ciEXPORT_SYMBOL(inet_del_protocol);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ciint inet_del_offload(const struct net_offload *prot, unsigned char protocol)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	int ret;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	ret = (cmpxchg((const struct net_offload **)&inet_offloads[protocol],
6462306a36Sopenharmony_ci		       prot, NULL) == prot) ? 0 : -1;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	synchronize_net();
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	return ret;
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ciEXPORT_SYMBOL(inet_del_offload);
71