18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * File: sysctl.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Phonet /proc/sys/net/phonet interface implementation
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Author: Rémi Denis-Courmont
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/seqlock.h>
138c2ecf20Sopenharmony_ci#include <linux/sysctl.h>
148c2ecf20Sopenharmony_ci#include <linux/errno.h>
158c2ecf20Sopenharmony_ci#include <linux/init.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <net/sock.h>
188c2ecf20Sopenharmony_ci#include <linux/phonet.h>
198c2ecf20Sopenharmony_ci#include <net/phonet/phonet.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define DYNAMIC_PORT_MIN	0x40
228c2ecf20Sopenharmony_ci#define DYNAMIC_PORT_MAX	0x7f
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic DEFINE_SEQLOCK(local_port_range_lock);
258c2ecf20Sopenharmony_cistatic int local_port_range_min[2] = {0, 0};
268c2ecf20Sopenharmony_cistatic int local_port_range_max[2] = {1023, 1023};
278c2ecf20Sopenharmony_cistatic int local_port_range[2] = {DYNAMIC_PORT_MIN, DYNAMIC_PORT_MAX};
288c2ecf20Sopenharmony_cistatic struct ctl_table_header *phonet_table_hrd;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic void set_local_port_range(int range[2])
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	write_seqlock(&local_port_range_lock);
338c2ecf20Sopenharmony_ci	local_port_range[0] = range[0];
348c2ecf20Sopenharmony_ci	local_port_range[1] = range[1];
358c2ecf20Sopenharmony_ci	write_sequnlock(&local_port_range_lock);
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_civoid phonet_get_local_port_range(int *min, int *max)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	unsigned int seq;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	do {
438c2ecf20Sopenharmony_ci		seq = read_seqbegin(&local_port_range_lock);
448c2ecf20Sopenharmony_ci		if (min)
458c2ecf20Sopenharmony_ci			*min = local_port_range[0];
468c2ecf20Sopenharmony_ci		if (max)
478c2ecf20Sopenharmony_ci			*max = local_port_range[1];
488c2ecf20Sopenharmony_ci	} while (read_seqretry(&local_port_range_lock, seq));
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic int proc_local_port_range(struct ctl_table *table, int write,
528c2ecf20Sopenharmony_ci				 void *buffer, size_t *lenp, loff_t *ppos)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	int ret;
558c2ecf20Sopenharmony_ci	int range[2] = {local_port_range[0], local_port_range[1]};
568c2ecf20Sopenharmony_ci	struct ctl_table tmp = {
578c2ecf20Sopenharmony_ci		.data = &range,
588c2ecf20Sopenharmony_ci		.maxlen = sizeof(range),
598c2ecf20Sopenharmony_ci		.mode = table->mode,
608c2ecf20Sopenharmony_ci		.extra1 = &local_port_range_min,
618c2ecf20Sopenharmony_ci		.extra2 = &local_port_range_max,
628c2ecf20Sopenharmony_ci	};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	if (write && ret == 0) {
678c2ecf20Sopenharmony_ci		if (range[1] < range[0])
688c2ecf20Sopenharmony_ci			ret = -EINVAL;
698c2ecf20Sopenharmony_ci		else
708c2ecf20Sopenharmony_ci			set_local_port_range(range);
718c2ecf20Sopenharmony_ci	}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	return ret;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic struct ctl_table phonet_table[] = {
778c2ecf20Sopenharmony_ci	{
788c2ecf20Sopenharmony_ci		.procname	= "local_port_range",
798c2ecf20Sopenharmony_ci		.data		= &local_port_range,
808c2ecf20Sopenharmony_ci		.maxlen		= sizeof(local_port_range),
818c2ecf20Sopenharmony_ci		.mode		= 0644,
828c2ecf20Sopenharmony_ci		.proc_handler	= proc_local_port_range,
838c2ecf20Sopenharmony_ci	},
848c2ecf20Sopenharmony_ci	{ }
858c2ecf20Sopenharmony_ci};
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ciint __init phonet_sysctl_init(void)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	phonet_table_hrd = register_net_sysctl(&init_net, "net/phonet", phonet_table);
908c2ecf20Sopenharmony_ci	return phonet_table_hrd == NULL ? -ENOMEM : 0;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_civoid phonet_sysctl_exit(void)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	unregister_net_sysctl_table(phonet_table_hrd);
968c2ecf20Sopenharmony_ci}
97