162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * File: sysctl.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Phonet /proc/sys/net/phonet interface implementation
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Author: Rémi Denis-Courmont
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/seqlock.h>
1362306a36Sopenharmony_ci#include <linux/sysctl.h>
1462306a36Sopenharmony_ci#include <linux/errno.h>
1562306a36Sopenharmony_ci#include <linux/init.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <net/sock.h>
1862306a36Sopenharmony_ci#include <linux/phonet.h>
1962306a36Sopenharmony_ci#include <net/phonet/phonet.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define DYNAMIC_PORT_MIN	0x40
2262306a36Sopenharmony_ci#define DYNAMIC_PORT_MAX	0x7f
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic DEFINE_SEQLOCK(local_port_range_lock);
2562306a36Sopenharmony_cistatic int local_port_range_min[2] = {0, 0};
2662306a36Sopenharmony_cistatic int local_port_range_max[2] = {1023, 1023};
2762306a36Sopenharmony_cistatic int local_port_range[2] = {DYNAMIC_PORT_MIN, DYNAMIC_PORT_MAX};
2862306a36Sopenharmony_cistatic struct ctl_table_header *phonet_table_hrd;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic void set_local_port_range(int range[2])
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	write_seqlock(&local_port_range_lock);
3362306a36Sopenharmony_ci	local_port_range[0] = range[0];
3462306a36Sopenharmony_ci	local_port_range[1] = range[1];
3562306a36Sopenharmony_ci	write_sequnlock(&local_port_range_lock);
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_civoid phonet_get_local_port_range(int *min, int *max)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	unsigned int seq;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	do {
4362306a36Sopenharmony_ci		seq = read_seqbegin(&local_port_range_lock);
4462306a36Sopenharmony_ci		if (min)
4562306a36Sopenharmony_ci			*min = local_port_range[0];
4662306a36Sopenharmony_ci		if (max)
4762306a36Sopenharmony_ci			*max = local_port_range[1];
4862306a36Sopenharmony_ci	} while (read_seqretry(&local_port_range_lock, seq));
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic int proc_local_port_range(struct ctl_table *table, int write,
5262306a36Sopenharmony_ci				 void *buffer, size_t *lenp, loff_t *ppos)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	int ret;
5562306a36Sopenharmony_ci	int range[2] = {local_port_range[0], local_port_range[1]};
5662306a36Sopenharmony_ci	struct ctl_table tmp = {
5762306a36Sopenharmony_ci		.data = &range,
5862306a36Sopenharmony_ci		.maxlen = sizeof(range),
5962306a36Sopenharmony_ci		.mode = table->mode,
6062306a36Sopenharmony_ci		.extra1 = &local_port_range_min,
6162306a36Sopenharmony_ci		.extra2 = &local_port_range_max,
6262306a36Sopenharmony_ci	};
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	if (write && ret == 0) {
6762306a36Sopenharmony_ci		if (range[1] < range[0])
6862306a36Sopenharmony_ci			ret = -EINVAL;
6962306a36Sopenharmony_ci		else
7062306a36Sopenharmony_ci			set_local_port_range(range);
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	return ret;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic struct ctl_table phonet_table[] = {
7762306a36Sopenharmony_ci	{
7862306a36Sopenharmony_ci		.procname	= "local_port_range",
7962306a36Sopenharmony_ci		.data		= &local_port_range,
8062306a36Sopenharmony_ci		.maxlen		= sizeof(local_port_range),
8162306a36Sopenharmony_ci		.mode		= 0644,
8262306a36Sopenharmony_ci		.proc_handler	= proc_local_port_range,
8362306a36Sopenharmony_ci	},
8462306a36Sopenharmony_ci	{ }
8562306a36Sopenharmony_ci};
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ciint __init phonet_sysctl_init(void)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	phonet_table_hrd = register_net_sysctl(&init_net, "net/phonet", phonet_table);
9062306a36Sopenharmony_ci	return phonet_table_hrd == NULL ? -ENOMEM : 0;
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_civoid phonet_sysctl_exit(void)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	unregister_net_sysctl_table(phonet_table_hrd);
9662306a36Sopenharmony_ci}
97