static int schar_read_proc_2(ctl_table *ctl, int write, struct file *file, void *buffer, size_t *lenp, loff_t *ppos) { int len = 0; /* someone is writing data to us */ /* v2.6remove if (write) { char *tmp = (char *) get_free_page(GFP_KERNEL); // MSG("proc: someone wrote %u bytes\n", (unsigned)*lenp); if (tmp) { free_page((unsigned long)tmp); file->f_pos += *lenp; } return 0; } v2.6remove */ len += sprintf(schar_proc_string_2, "GIGABIT DRIVER ETH2: mm \n\n"); len += sprintf(schar_proc_string_2+len, " RECEIVE: \n"); len += sprintf(schar_proc_string_2+len, " recieve\t\t%ld packets\n",proc_rpackets_2); len += sprintf(schar_proc_string_2+len, " receive \t\t\t%04d%09ld bytes\n",proc_rbytesH_2,proc_rbytesL_2); len += sprintf(schar_proc_string_2+len, " memory \t\t\t%09ld bytes\n",(BIGPHYS_PAGES_2*PAGE_SIZE)); len += sprintf(schar_proc_string_2+len, " rate \t\t\t%6ld KBits/s \n",proc_rate_2/1000); len += sprintf(schar_proc_string_2+len, " loop %d count %ld\n\n",ring_loop_2,ring_pnt_2); len += sprintf(schar_proc_string_2+len, " TRANSMIT: \n"); len += sprintf(schar_proc_string_2+len, " transmit\t\t%d packets \n",proc_tpackets_2); len += sprintf(schar_proc_string_2+len, " transmit \t\t\t%d%08ld bytes\n\n",proc_tbytesH_2,proc_tbytesL_2); len += sprintf(schar_proc_string_2+len, " ERROR STATISTICS: \n"); len += sprintf(schar_proc_string_2+len, " missing packets \t\t%ld\n",proc_pmissing_2); *lenp = len; return proc_dostring(ctl, write, file, buffer, lenp, ppos); }
static int schar_read_proc(ctl_table *ctl, int write, struct file *file, void *buffer, size_t *lenp, loff_t *ppos) { int len = 0; // MSG("proc: %s\n", write ? "write" : "read"); /* someone is writing data to us */ /* v2.6remove if (write) { char *tmp = (char *) get_free_page(GFP_KERNEL); // MSG("proc: someone wrote %u bytes\n", (unsigned)*lenp); if (tmp) { free_page((unsigned long)tmp); file->f_pos += *lenp; } return 0; } v2.6remove */ len += sprintf(schar_proc_string, "GIGABIT DRIVER SIMPLE JTAG\n\n"); len += sprintf(schar_proc_string+len, " LEFT TO READ: \n"); len += sprintf(schar_proc_string+len," pack_left\t\t%d packets\n",pack_left); len += sprintf(schar_proc_string+len, " RECEIVE: \n"); len += sprintf(schar_proc_string+len, " recieve\t\t%ld packets\n",proc_rpackets); len += sprintf(schar_proc_string+len, " receive \t\t\t%02d%09ld bytes\n",proc_rbytesH,proc_rbytesL); len += sprintf(schar_proc_string+len, " memory \t\t\t%09d bytes\n",MMT_BUF_SIZE); len += sprintf(schar_proc_string+len, " TRANSMIT: \n"); len += sprintf(schar_proc_string+len, " transmit\t\t%d packets \n",proc_tpackets); len += sprintf(schar_proc_string+len, " transmit \t\t\t%02d%09ld bytes\n\n",proc_tbytesH,proc_tbytesL); *lenp = len; return proc_dostring(ctl, write, file, buffer, lenp, ppos); }
static int proc_tcp_fastopen_key(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { struct ctl_table tbl = { .maxlen = (TCP_FASTOPEN_KEY_LENGTH * 2 + 10) }; struct tcp_fastopen_context *ctxt; int ret; u32 user_key[4]; /* 16 bytes, matching TCP_FASTOPEN_KEY_LENGTH */ tbl.data = kmalloc(tbl.maxlen, GFP_KERNEL); if (!tbl.data) return -ENOMEM; rcu_read_lock(); ctxt = rcu_dereference(tcp_fastopen_ctx); if (ctxt) memcpy(user_key, ctxt->key, TCP_FASTOPEN_KEY_LENGTH); else memset(user_key, 0, sizeof(user_key)); rcu_read_unlock(); snprintf(tbl.data, tbl.maxlen, "%08x-%08x-%08x-%08x", user_key[0], user_key[1], user_key[2], user_key[3]); ret = proc_dostring(&tbl, write, buffer, lenp, ppos); if (write && ret == 0) { if (sscanf(tbl.data, "%x-%x-%x-%x", user_key, user_key + 1, user_key + 2, user_key + 3) != 4) { ret = -EINVAL; goto bad_key; } /* Generate a dummy secret but don't publish it. This * is needed so we don't regenerate a new key on the * first invocation of tcp_fastopen_cookie_gen */ tcp_fastopen_init_key_once(false); tcp_fastopen_reset_cipher(user_key, TCP_FASTOPEN_KEY_LENGTH); } bad_key: pr_debug("proc FO key set 0x%x-%x-%x-%x <- 0x%s: %u\n", user_key[0], user_key[1], user_key[2], user_key[3], (char *)tbl.data, ret); kfree(tbl.data); return ret; } static struct ctl_table ipv4_table[] = { { .procname = "tcp_timestamps", .data = &sysctl_tcp_timestamps, .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec }, { .procname = "tcp_window_scaling",
// TODO: functions are almost the same, should abstract them to wrapper with proc_handler and data printer parameters static int t1_proc_dostring(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int result; LOG(LC_PROC, 0, "t1: gona read %s from procfs entry, old value is: %s\n", table->procname, (char*)table->data); result = proc_dostring(table, write, buffer, lenp, ppos); LOG(LC_PROC, 0, "t1: read %s from procfs, new value is: %s\n", table->procname, (char*)table->data); return result; }
/* /proc/sys entry point for injecting up/down nid event * <up|down> <nid> */ static int proc_peer_state(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int rc; int nid; int node_down; char command[10]; ENTRY; rc = proc_dostring(table, write, buffer, lenp, ppos); if (!write) { /* read */ RETURN(rc); } if (kgnilnd_data.kgn_init != GNILND_INIT_ALL) { rc = -EINVAL; RETURN(rc); } /* convert to nid, up/down values */ rc = sscanf(kgnilnd_sysctl.ksd_peer_state, "%s %d", command, &nid); CDEBUG(D_INFO, "command %s, nid %d\n", command, nid); if (rc != 2) { CDEBUG(D_ERROR, "invalid parameter\n"); RETURN(rc); } else { switch (command[0]) { case 'd': /* down */ node_down = 1; CDEBUG(D_INFO, "take node %d down\n", nid); break; case 'u': /* up */ node_down = 0; CDEBUG(D_INFO, "bring node %d up\n", nid); break; default: CDEBUG(D_ERROR, "invalid command %s\n", command); RETURN(-EINVAL); } } CDEBUG(D_INFO, "proc_peer_state: reporting node_down %d, nid %d\n", node_down, nid); rc = kgnilnd_report_node_state(nid, node_down); if (rc) { rc = -EINVAL; } RETURN(rc); }
static int nf_log_proc_dostring(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { const struct nf_logger *logger; char buf[NFLOGGER_NAME_LEN]; int r = 0; int tindex = (unsigned long)table->extra1; struct net *net = current->nsproxy->net_ns; if (write) { struct ctl_table tmp = *table; tmp.data = buf; r = proc_dostring(&tmp, write, buffer, lenp, ppos); if (r) return r; if (!strcmp(buf, "NONE")) { nf_log_unbind_pf(net, tindex); return 0; } mutex_lock(&nf_log_mutex); logger = __find_logger(tindex, buf); if (logger == NULL) { mutex_unlock(&nf_log_mutex); return -ENOENT; } rcu_assign_pointer(net->nf.nf_loggers[tindex], logger); mutex_unlock(&nf_log_mutex); } else { mutex_lock(&nf_log_mutex); logger = nft_log_dereference(net->nf.nf_loggers[tindex]); if (!logger) table->data = "NONE"; else table->data = logger->name; r = proc_dostring(table, write, buffer, lenp, ppos); mutex_unlock(&nf_log_mutex); } return r; }
/* * Special case of dostring for the UTS structure. This has locks * to observe. Should this be in kernel/sys.c ???? */ static int proc_do_uts_string(ctl_table *table, int write, struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos) { struct ctl_table uts_table; int r; memcpy(&uts_table, table, sizeof(uts_table)); uts_table.data = get_uts(table, write); r = proc_dostring(&uts_table,write,filp,buffer,lenp, ppos); put_uts(table, write, uts_table.data); return r; }
static int proc_do_rss_key(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { struct ctl_table fake_table; char buf[NETDEV_RSS_KEY_LEN * 3]; snprintf(buf, sizeof(buf), "%*phC", NETDEV_RSS_KEY_LEN, netdev_rss_key); fake_table.data = buf; fake_table.maxlen = sizeof(buf); return proc_dostring(&fake_table, write, buffer, lenp, ppos); }
/* Validate changes from /proc interface. */ static int ipv4_ping_group_range(ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { struct user_namespace *user_ns = current_user_ns(); int ret; gid_t urange[2]; kgid_t low, high; ctl_table tmp = { .data = &urange, .maxlen = sizeof(urange), .mode = table->mode, .extra1 = &ip_ping_group_range_min, .extra2 = &ip_ping_group_range_max, }; inet_get_ping_group_range_table(table, &low, &high); urange[0] = from_kgid_munged(user_ns, low); urange[1] = from_kgid_munged(user_ns, high); ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); if (write && ret == 0) { low = make_kgid(user_ns, urange[0]); high = make_kgid(user_ns, urange[1]); if (!gid_valid(low) || !gid_valid(high) || (urange[1] < urange[0]) || gid_lt(high, low)) { low = make_kgid(&init_user_ns, 1); high = make_kgid(&init_user_ns, 0); } set_ping_group_range(table, low, high); } return ret; } static int proc_tcp_congestion_control(ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { char val[TCP_CA_NAME_MAX]; ctl_table tbl = { .data = val, .maxlen = TCP_CA_NAME_MAX, }; int ret; tcp_get_default_congestion_control(val); ret = proc_dostring(&tbl, write, buffer, lenp, ppos); if (write && ret == 0) ret = tcp_set_default_congestion_control(val); return ret; }
/* And the same for proc */ int proc_dolasatstring(ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos) { int r; r = proc_dostring(table, write, buffer, lenp, ppos); if ((!write) || r) return r; lasat_write_eeprom_info(); return 0; }
/* And the same for proc */ int proc_dolasatstring(ctl_table *table, int write, struct file *filp, void *buffer, size_t *lenp, loff_t *ppos) { int r; mutex_lock(&lasat_info_mutex); r = proc_dostring(table, write, filp, buffer, lenp, ppos); if ( (!write) || r) { mutex_unlock(&lasat_info_mutex); return r; } lasat_write_eeprom_info(); mutex_unlock(&lasat_info_mutex); return 0; }
static int do_devname(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int ret; ret = proc_dostring(table, write, buffer, lenp, ppos); if (ret == 0 && write) { struct ias_value *val; val = irias_new_string_value(sysctl_devname); if (val) irias_object_change_attribute("Device", "DeviceName", val); } return ret; }
/* * Special case of dostring for the UTS structure. This has locks * to observe. Should this be in kernel/sys.c ???? */ static int proc_do_uts_string(ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { struct ctl_table uts_table; int r; memcpy(&uts_table, table, sizeof(uts_table)); uts_table.data = get_uts(table, write); r = proc_dostring(&uts_table,write,buffer,lenp, ppos); put_uts(table, write, uts_table.data); if (write) proc_sys_poll_notify(table->poll); return r; }
static int do_devname(ctl_table *table, int write, struct file *filp, void *buffer, size_t *lenp) { int ret; ret = proc_dostring(table, write, filp, buffer, lenp); if (ret == 0 && write) { struct ias_value *val; val = irias_new_string_value(sysctl_devname); if (val) irias_object_change_attribute("Device", "DeviceName", val); } return ret; }
static int proc_tcp_available_congestion_control(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { struct ctl_table tbl = { .maxlen = TCP_CA_BUF_MAX, }; int ret; tbl.data = kmalloc(tbl.maxlen, GFP_USER); if (!tbl.data) return -ENOMEM; tcp_get_available_congestion_control(tbl.data, TCP_CA_BUF_MAX); ret = proc_dostring(&tbl, write, buffer, lenp, ppos); kfree(tbl.data); return ret; }
static int proc_sctp_do_hmac_alg(ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { struct net *net = current->nsproxy->net_ns; char tmp[8]; ctl_table tbl; int ret; int changed = 0; char *none = "none"; memset(&tbl, 0, sizeof(struct ctl_table)); if (write) { tbl.data = tmp; tbl.maxlen = 8; } else { tbl.data = net->sctp.sctp_hmac_alg ? : none; tbl.maxlen = strlen(tbl.data); } ret = proc_dostring(&tbl, write, buffer, lenp, ppos); if (write) { #ifdef CONFIG_CRYPTO_MD5 if (!strncmp(tmp, "md5", 3)) { net->sctp.sctp_hmac_alg = "md5"; changed = 1; } #endif #ifdef CONFIG_CRYPTO_SHA1 if (!strncmp(tmp, "sha1", 4)) { net->sctp.sctp_hmac_alg = "sha1"; changed = 1; } #endif if (!strncmp(tmp, "none", 4)) { net->sctp.sctp_hmac_alg = NULL; changed = 1; } if (!changed) ret = -EINVAL; } return ret; }
static int proc_tcp_congestion_control(ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { char val[TCP_CA_NAME_MAX]; ctl_table tbl = { .data = val, .maxlen = TCP_CA_NAME_MAX, }; int ret; tcp_get_default_congestion_control(val); ret = proc_dostring(&tbl, write, buffer, lenp, ppos); if (write && ret == 0) ret = tcp_set_default_congestion_control(val); return ret; }
static int set_default_qdisc(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { char id[IFNAMSIZ]; struct ctl_table tbl = { .data = id, .maxlen = IFNAMSIZ, }; int ret; qdisc_get_default(id, IFNAMSIZ); ret = proc_dostring(&tbl, write, buffer, lenp, ppos); if (write && ret == 0) ret = qdisc_set_default(id); return ret; }
/* Validate changes from /proc interface. */ static int ipv4_local_port_range(ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int ret; int range[2]; ctl_table tmp = { .data = &range, .maxlen = sizeof(range), .mode = table->mode, .extra1 = &ip_local_port_range_min, .extra2 = &ip_local_port_range_max, }; inet_get_local_port_range(range, range + 1); ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); if (write && ret == 0) { if (range[1] < range[0]) ret = -EINVAL; else set_local_port_range(range); } return ret; } static int proc_tcp_congestion_control(ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { char val[TCP_CA_NAME_MAX]; ctl_table tbl = { .data = val, .maxlen = TCP_CA_NAME_MAX, }; int ret; tcp_get_default_congestion_control(val); ret = proc_dostring(&tbl, write, buffer, lenp, ppos); if (write && ret == 0) ret = tcp_set_default_congestion_control(val); return ret; }
static int utsmod_proc_do_string(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { struct ctl_table utsmod_table; int r; if (write) { // init_uts_ns is read only if (current->nsproxy->uts_ns == &init_uts_ns) return -EPERM; // require CAP_SYS_ADMIN if (!ns_capable(current->nsproxy->uts_ns->user_ns, CAP_SYS_ADMIN)) return -EPERM; } // update current utsns memcpy(&utsmod_table, table, sizeof(utsmod_table)); utsmod_table.data = utsmod_get_uts(table, write); r = proc_dostring(&utsmod_table, write, buffer, lenp, ppos); utsmod_put_uts(table, write, utsmod_table.data); return r; }
/* * Special case of dostring for the UTS structure. This has locks * to observe. Should this be in kernel/sys.c ???? */ static int proc_do_uts_string(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { struct ctl_table uts_table; int r; char tmp_data[__NEW_UTS_LEN + 1]; memcpy(&uts_table, table, sizeof(uts_table)); uts_table.data = tmp_data; /* * Buffer the value in tmp_data so that proc_dostring() can be called * without holding any locks. * We also need to read the original value in the write==1 case to * support partial writes. */ down_read(&uts_sem); memcpy(tmp_data, get_uts(table), sizeof(tmp_data)); up_read(&uts_sem); r = proc_dostring(&uts_table, write, buffer, lenp, ppos); if (write) { /* * Write back the new value. * Note that, since we dropped uts_sem, the result can * theoretically be incorrect if there are two parallel writes * at non-zero offsets to the same sysctl. */ down_write(&uts_sem); memcpy(get_uts(table), tmp_data, sizeof(tmp_data)); up_write(&uts_sem); proc_sys_poll_notify(table->poll); } return r; }
static int proc_tcp_fastopen_key(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { struct ctl_table tbl = { .maxlen = (TCP_FASTOPEN_KEY_LENGTH * 2 + 10) }; struct tcp_fastopen_context *ctxt; int ret; u32 user_key[4]; /* 16 bytes, matching TCP_FASTOPEN_KEY_LENGTH */ tbl.data = kmalloc(tbl.maxlen, GFP_KERNEL); if (!tbl.data) return -ENOMEM; rcu_read_lock(); ctxt = rcu_dereference(tcp_fastopen_ctx); if (ctxt) memcpy(user_key, ctxt->key, TCP_FASTOPEN_KEY_LENGTH); else memset(user_key, 0, sizeof(user_key)); rcu_read_unlock(); snprintf(tbl.data, tbl.maxlen, "%08x-%08x-%08x-%08x", user_key[0], user_key[1], user_key[2], user_key[3]); ret = proc_dostring(&tbl, write, buffer, lenp, ppos); if (write && ret == 0) { if (sscanf(tbl.data, "%x-%x-%x-%x", user_key, user_key + 1, user_key + 2, user_key + 3) != 4) { ret = -EINVAL; goto bad_key; } /* Generate a dummy secret but don't publish it. This * is needed so we don't regenerate a new key on the * first invocation of tcp_fastopen_cookie_gen */ tcp_fastopen_init_key_once(false); tcp_fastopen_reset_cipher(user_key, TCP_FASTOPEN_KEY_LENGTH); } bad_key: pr_debug("proc FO key set 0x%x-%x-%x-%x <- 0x%s: %u\n", user_key[0], user_key[1], user_key[2], user_key[3], (char *)tbl.data, ret); kfree(tbl.data); return ret; } static void proc_configure_early_demux(int enabled, int protocol) { struct net_protocol *ipprot; #if IS_ENABLED(CONFIG_IPV6) struct inet6_protocol *ip6prot; #endif rcu_read_lock(); ipprot = rcu_dereference(inet_protos[protocol]); if (ipprot) ipprot->early_demux = enabled ? ipprot->early_demux_handler : NULL; #if IS_ENABLED(CONFIG_IPV6) ip6prot = rcu_dereference(inet6_protos[protocol]); if (ip6prot) ip6prot->early_demux = enabled ? ip6prot->early_demux_handler : NULL; #endif rcu_read_unlock(); } static int proc_tcp_early_demux(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int ret = 0; ret = proc_dointvec(table, write, buffer, lenp, ppos); if (write && !ret) { int enabled = init_net.ipv4.sysctl_tcp_early_demux; proc_configure_early_demux(enabled, IPPROTO_TCP); } return ret; }
/* Validate changes from /proc interface. */ static int ipv4_local_port_range(ctl_table *table, int write, struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos) { int ret; int range[2] = { sysctl_local_port_range[0], sysctl_local_port_range[1] }; ctl_table tmp = { .data = &range, .maxlen = sizeof(range), .mode = table->mode, .extra1 = &ip_local_port_range_min, .extra2 = &ip_local_port_range_max, }; ret = proc_dointvec_minmax(&tmp, write, filp, buffer, lenp, ppos); if (write && ret == 0) { if (range[1] < range[0]) ret = -EINVAL; else set_local_port_range(range); } return ret; } /* Validate changes from sysctl interface. */ static int ipv4_sysctl_local_port_range(ctl_table *table, int __user *name, int nlen, void __user *oldval, size_t __user *oldlenp, void __user *newval, size_t newlen) { int ret; int range[2] = { sysctl_local_port_range[0], sysctl_local_port_range[1] }; ctl_table tmp = { .data = &range, .maxlen = sizeof(range), .mode = table->mode, .extra1 = &ip_local_port_range_min, .extra2 = &ip_local_port_range_max, }; ret = sysctl_intvec(&tmp, name, nlen, oldval, oldlenp, newval, newlen); if (ret == 0 && newval && newlen) { if (range[1] < range[0]) ret = -EINVAL; else set_local_port_range(range); } return ret; } static int proc_tcp_congestion_control(ctl_table *ctl, int write, struct file * filp, void __user *buffer, size_t *lenp, loff_t *ppos) { char val[TCP_CA_NAME_MAX]; ctl_table tbl = { .data = val, .maxlen = TCP_CA_NAME_MAX, }; int ret; tcp_get_default_congestion_control(val); ret = proc_dostring(&tbl, write, filp, buffer, lenp, ppos); if (write && ret == 0) ret = tcp_set_default_congestion_control(val); return ret; }
static int adam2_setenv_sysctl(ctl_table *ctl, int write, struct file * filp, void *buffer, size_t *lenp) { char *var, *val, *ptr; int ret, i; size_t total, len; if (!*lenp || (filp->f_pos && !write)) { *lenp = 0; return 0; } if(ctl->ctl_name != DEV_ADAM2_ENV) { return -ENODEV; } if(write) { ret = proc_dostring(ctl, write, filp, buffer, lenp); ptr = strpbrk(info, " \t"); if(!ptr) { /* We have no way to distinguish between unsetting a * variable and setting a non-value variable. * * Consequently, we will treat any variable * without a value as an unset request. */ adam2_env_unset_variable(info); } else { /* Set the variable-value pair in flash */ *ptr++ = 0; if(adam2_env_set_variable(info, ptr) != 0) { printk(KERN_NOTICE "Defragging the environment variable region.\n"); adam2_env_defrag(); if( adam2_env_set_variable(info, ptr) != 0 ) printk(KERN_WARNING "Failed to write %s to environment variable region.\n", info); } } } else { total=0; len=0; /* Scan thru the flash, looking for each possible variable index */ for(i = 0; i < MAX_ENV_ENTRY; i++) { if( (var=adam2_env_get_variable(i))!=NULL ) { if( (val=adam2_env_get_value(var)) != NULL) { len = sprintf(info, "%s\t%s\n", var, val); kfree(val); } kfree(var); if(len > *lenp - total) len = *lenp - total; if(len) { if(copy_to_user(buffer+total, info, len)) return -EFAULT; } else { break; } total += len; } } *lenp = total; filp->f_pos += total; } return 0; }
static int ipv4_tcp_mem(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int ret; unsigned long vec[3]; struct net *net = current->nsproxy->net_ns; #ifdef CONFIG_MEMCG_KMEM struct mem_cgroup *memcg; #endif struct ctl_table tmp = { .data = &vec, .maxlen = sizeof(vec), .mode = ctl->mode, }; if (!write) { ctl->data = &net->ipv4.sysctl_tcp_mem; return proc_doulongvec_minmax(ctl, write, buffer, lenp, ppos); } ret = proc_doulongvec_minmax(&tmp, write, buffer, lenp, ppos); if (ret) return ret; #ifdef CONFIG_MEMCG_KMEM rcu_read_lock(); memcg = mem_cgroup_from_task(current); tcp_prot_mem(memcg, vec[0], 0); tcp_prot_mem(memcg, vec[1], 1); tcp_prot_mem(memcg, vec[2], 2); rcu_read_unlock(); #endif net->ipv4.sysctl_tcp_mem[0] = vec[0]; net->ipv4.sysctl_tcp_mem[1] = vec[1]; net->ipv4.sysctl_tcp_mem[2] = vec[2]; return 0; } static int proc_tcp_fastopen_key(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { struct ctl_table tbl = { .maxlen = (TCP_FASTOPEN_KEY_LENGTH * 2 + 10) }; struct tcp_fastopen_context *ctxt; int ret; u32 user_key[4]; /* 16 bytes, matching TCP_FASTOPEN_KEY_LENGTH */ tbl.data = kmalloc(tbl.maxlen, GFP_KERNEL); if (!tbl.data) return -ENOMEM; rcu_read_lock(); ctxt = rcu_dereference(tcp_fastopen_ctx); if (ctxt) memcpy(user_key, ctxt->key, TCP_FASTOPEN_KEY_LENGTH); else memset(user_key, 0, sizeof(user_key)); rcu_read_unlock(); snprintf(tbl.data, tbl.maxlen, "%08x-%08x-%08x-%08x", user_key[0], user_key[1], user_key[2], user_key[3]); ret = proc_dostring(&tbl, write, buffer, lenp, ppos); if (write && ret == 0) { if (sscanf(tbl.data, "%x-%x-%x-%x", user_key, user_key + 1, user_key + 2, user_key + 3) != 4) { ret = -EINVAL; goto bad_key; } tcp_fastopen_reset_cipher(user_key, TCP_FASTOPEN_KEY_LENGTH); } bad_key: pr_debug("proc FO key set 0x%x-%x-%x-%x <- 0x%s: %u\n", user_key[0], user_key[1], user_key[2], user_key[3], (char *)tbl.data, ret); kfree(tbl.data); return ret; } static struct ctl_table ipv4_table[] = { { .procname = "tcp_timestamps", .data = &sysctl_tcp_timestamps, .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec }, { .procname = "tcp_window_scaling", .data = &sysctl_tcp_window_scaling, .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec }, { .procname = "tcp_sack",
/* Validate changes from /proc interface. */ static int ipv4_ping_group_range(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { struct user_namespace *user_ns = current_user_ns(); int ret; gid_t urange[2]; kgid_t low, high; struct ctl_table tmp = { .data = &urange, .maxlen = sizeof(urange), .mode = table->mode, .extra1 = &ip_ping_group_range_min, .extra2 = &ip_ping_group_range_max, }; inet_get_ping_group_range_table(table, &low, &high); urange[0] = from_kgid_munged(user_ns, low); urange[1] = from_kgid_munged(user_ns, high); ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); if (write && ret == 0) { low = make_kgid(user_ns, urange[0]); high = make_kgid(user_ns, urange[1]); if (!gid_valid(low) || !gid_valid(high)) return -EINVAL; if (urange[1] < urange[0] || gid_lt(high, low)) { low = make_kgid(&init_user_ns, 1); high = make_kgid(&init_user_ns, 0); } set_ping_group_range(table, low, high); } return ret; } static int ipv4_fwd_update_priority(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { struct net *net; int ret; net = container_of(table->data, struct net, ipv4.sysctl_ip_fwd_update_priority); ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); if (write && ret == 0) call_netevent_notifiers(NETEVENT_IPV4_FWD_UPDATE_PRIORITY_UPDATE, net); return ret; } static int proc_tcp_congestion_control(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { struct net *net = container_of(ctl->data, struct net, ipv4.tcp_congestion_control); char val[TCP_CA_NAME_MAX]; struct ctl_table tbl = { .data = val, .maxlen = TCP_CA_NAME_MAX, }; int ret; tcp_get_default_congestion_control(net, val); ret = proc_dostring(&tbl, write, buffer, lenp, ppos); if (write && ret == 0) ret = tcp_set_default_congestion_control(net, val); return ret; } static int proc_tcp_available_congestion_control(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { struct ctl_table tbl = { .maxlen = TCP_CA_BUF_MAX, }; int ret; tbl.data = kmalloc(tbl.maxlen, GFP_USER); if (!tbl.data) return -ENOMEM; tcp_get_available_congestion_control(tbl.data, TCP_CA_BUF_MAX); ret = proc_dostring(&tbl, write, buffer, lenp, ppos); kfree(tbl.data); return ret; } static int proc_allowed_congestion_control(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { struct ctl_table tbl = { .maxlen = TCP_CA_BUF_MAX }; int ret; tbl.data = kmalloc(tbl.maxlen, GFP_USER); if (!tbl.data) return -ENOMEM; tcp_get_allowed_congestion_control(tbl.data, tbl.maxlen); ret = proc_dostring(&tbl, write, buffer, lenp, ppos); if (write && ret == 0) ret = tcp_set_allowed_congestion_control(tbl.data); kfree(tbl.data); return ret; } static int proc_tcp_fastopen_key(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { struct net *net = container_of(table->data, struct net, ipv4.sysctl_tcp_fastopen); struct ctl_table tbl = { .maxlen = (TCP_FASTOPEN_KEY_LENGTH * 2 + 10) }; struct tcp_fastopen_context *ctxt; u32 user_key[4]; /* 16 bytes, matching TCP_FASTOPEN_KEY_LENGTH */ __le32 key[4]; int ret, i; tbl.data = kmalloc(tbl.maxlen, GFP_KERNEL); if (!tbl.data) return -ENOMEM; rcu_read_lock(); ctxt = rcu_dereference(net->ipv4.tcp_fastopen_ctx); if (ctxt) memcpy(key, ctxt->key, TCP_FASTOPEN_KEY_LENGTH); else memset(key, 0, sizeof(key)); rcu_read_unlock(); for (i = 0; i < ARRAY_SIZE(key); i++) user_key[i] = le32_to_cpu(key[i]); snprintf(tbl.data, tbl.maxlen, "%08x-%08x-%08x-%08x", user_key[0], user_key[1], user_key[2], user_key[3]); ret = proc_dostring(&tbl, write, buffer, lenp, ppos); if (write && ret == 0) { if (sscanf(tbl.data, "%x-%x-%x-%x", user_key, user_key + 1, user_key + 2, user_key + 3) != 4) { ret = -EINVAL; goto bad_key; } for (i = 0; i < ARRAY_SIZE(user_key); i++) key[i] = cpu_to_le32(user_key[i]); tcp_fastopen_reset_cipher(net, NULL, key, TCP_FASTOPEN_KEY_LENGTH); } bad_key: pr_debug("proc FO key set 0x%x-%x-%x-%x <- 0x%s: %u\n", user_key[0], user_key[1], user_key[2], user_key[3], (char *)tbl.data, ret); kfree(tbl.data); return ret; } static void proc_configure_early_demux(int enabled, int protocol) { struct net_protocol *ipprot; #if IS_ENABLED(CONFIG_IPV6) struct inet6_protocol *ip6prot; #endif rcu_read_lock(); ipprot = rcu_dereference(inet_protos[protocol]); if (ipprot) ipprot->early_demux = enabled ? ipprot->early_demux_handler : NULL; #if IS_ENABLED(CONFIG_IPV6) ip6prot = rcu_dereference(inet6_protos[protocol]); if (ip6prot) ip6prot->early_demux = enabled ? ip6prot->early_demux_handler : NULL; #endif rcu_read_unlock(); } static int proc_tcp_early_demux(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int ret = 0; ret = proc_dointvec(table, write, buffer, lenp, ppos); if (write && !ret) { int enabled = init_net.ipv4.sysctl_tcp_early_demux; proc_configure_early_demux(enabled, IPPROTO_TCP); } return ret; } static int proc_udp_early_demux(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int ret = 0; ret = proc_dointvec(table, write, buffer, lenp, ppos); if (write && !ret) { int enabled = init_net.ipv4.sysctl_udp_early_demux; proc_configure_early_demux(enabled, IPPROTO_UDP); } return ret; }