MT#57093 more granular kernel I/O structs

The unified `rtpengine_message` struct has become quite large, and most
fields are not used for most of the messages sent to the kernel module.

Use command-specific structures that only contain the required
information. Adapt the I/O code and the size checks to use the correct
sizes.

Do special handling for GET_*_STATS as these commands do both input and
output at the same time. Copy in only the input portion of the struct.
(Copy-out still copies the entire struct at this point.)

Change-Id: Ia8aec6135fd7de42ae4e62e7f2dc23804e1f0914
pull/1642/head
Richard Fuchs 3 years ago
parent 99fe5d4b9f
commit 8b4d337e86

@ -66,7 +66,7 @@ static int kernel_open_table(unsigned int id) {
char str[64];
int saved_errno;
int fd;
struct rtpengine_message msg;
struct rtpengine_command_noop cmd;
int i;
sprintf(str, PREFIX "/%u/control", id);
@ -74,11 +74,9 @@ static int kernel_open_table(unsigned int id) {
if (fd == -1)
return -1;
ZERO(msg);
msg.cmd = REMG_NOOP;
msg.u.noop.size = sizeof(msg);
msg.u.noop.last_cmd = __REMG_LAST;
i = write(fd, &msg, sizeof(msg));
cmd.cmd = REMG_NOOP;
cmd.noop.last_cmd = __REMG_LAST;
i = write(fd, &cmd, sizeof(cmd));
if (i <= 0)
goto fail;
@ -123,17 +121,16 @@ int kernel_setup_table(unsigned int id) {
int kernel_add_stream(struct rtpengine_target_info *mti) {
struct rtpengine_message msg;
struct rtpengine_command_add_target cmd;
int ret;
if (!kernel.is_open)
return -1;
msg.cmd = REMG_ADD_TARGET;
msg.u.target = *mti;
cmd.cmd = REMG_ADD_TARGET;
cmd.target = *mti;
// coverity[uninit_use_in_call : FALSE]
ret = write(kernel.fd, &msg, sizeof(msg));
ret = write(kernel.fd, &cmd, sizeof(cmd));
if (ret > 0)
return 0;
@ -142,17 +139,16 @@ int kernel_add_stream(struct rtpengine_target_info *mti) {
}
int kernel_add_destination(struct rtpengine_destination_info *mdi) {
struct rtpengine_message msg;
struct rtpengine_command_destination cmd;
int ret;
if (!kernel.is_open)
return -1;
msg.cmd = REMG_ADD_DESTINATION;
msg.u.destination = *mdi;
cmd.cmd = REMG_ADD_DESTINATION;
cmd.destination = *mdi;
// coverity[uninit_use_in_call : FALSE]
ret = write(kernel.fd, &msg, sizeof(msg));
ret = write(kernel.fd, &cmd, sizeof(cmd));
if (ret > 0)
return 0;
@ -162,17 +158,16 @@ int kernel_add_destination(struct rtpengine_destination_info *mdi) {
int kernel_del_stream(const struct re_address *a) {
struct rtpengine_message msg;
struct rtpengine_command_del_target cmd;
int ret;
if (!kernel.is_open)
return -1;
ZERO(msg);
msg.cmd = REMG_DEL_TARGET;
msg.u.target.local = *a;
cmd.cmd = REMG_DEL_TARGET;
cmd.local = *a;
ret = write(kernel.fd, &msg, sizeof(msg));
ret = write(kernel.fd, &cmd, sizeof(cmd));
if (ret > 0)
return 0;
@ -211,75 +206,71 @@ GList *kernel_list() {
}
unsigned int kernel_add_call(const char *id) {
struct rtpengine_message msg;
struct rtpengine_command_add_call cmd;
int ret;
if (!kernel.is_open)
return UNINIT_IDX;
ZERO(msg);
msg.cmd = REMG_ADD_CALL;
snprintf(msg.u.call.call_id, sizeof(msg.u.call.call_id), "%s", id);
cmd.cmd = REMG_ADD_CALL;
snprintf(cmd.call.call_id, sizeof(cmd.call.call_id), "%s", id);
ret = read(kernel.fd, &msg, sizeof(msg));
if (ret != sizeof(msg))
ret = read(kernel.fd, &cmd, sizeof(cmd));
if (ret != sizeof(cmd))
return UNINIT_IDX;
return msg.u.call.call_idx;
return cmd.call.call_idx;
}
int kernel_del_call(unsigned int idx) {
struct rtpengine_message msg;
struct rtpengine_command_del_call cmd;
int ret;
if (!kernel.is_open)
return -1;
ZERO(msg);
msg.cmd = REMG_DEL_CALL;
msg.u.call.call_idx = idx;
cmd.cmd = REMG_DEL_CALL;
cmd.call_idx = idx;
ret = write(kernel.fd, &msg, sizeof(msg));
if (ret != sizeof(msg))
ret = write(kernel.fd, &cmd, sizeof(cmd));
if (ret != sizeof(cmd))
return -1;
return 0;
}
unsigned int kernel_add_intercept_stream(unsigned int call_idx, const char *id) {
struct rtpengine_message msg;
struct rtpengine_command_add_stream cmd;
int ret;
if (!kernel.is_open)
return UNINIT_IDX;
ZERO(msg);
msg.cmd = REMG_ADD_STREAM;
msg.u.stream.call_idx = call_idx;
snprintf(msg.u.stream.stream_name, sizeof(msg.u.stream.stream_name), "%s", id);
cmd.cmd = REMG_ADD_STREAM;
cmd.stream.idx.call_idx = call_idx;
snprintf(cmd.stream.stream_name, sizeof(cmd.stream.stream_name), "%s", id);
ret = read(kernel.fd, &msg, sizeof(msg));
if (ret != sizeof(msg))
ret = read(kernel.fd, &cmd, sizeof(cmd));
if (ret != sizeof(cmd))
return UNINIT_IDX;
return msg.u.stream.stream_idx;
return cmd.stream.idx.stream_idx;
}
int kernel_update_stats(const struct re_address *a, struct rtpengine_stats_info *out) {
struct rtpengine_message msg;
struct rtpengine_command_stats cmd;
int ret;
if (!kernel.is_open)
return -1;
ZERO(msg);
msg.cmd = REMG_GET_RESET_STATS;
msg.u.stats.local = *a;
cmd.cmd = REMG_GET_RESET_STATS;
cmd.local = *a;
ret = read(kernel.fd, &msg, sizeof(msg));
ret = read(kernel.fd, &cmd, sizeof(cmd));
if (ret <= 0) {
ilog(LOG_ERROR, "Failed to get stream stats from kernel: %s", strerror(errno));
return -1;
}
*out = msg.u.stats;
*out = cmd.stats;
return 0;
}

@ -914,17 +914,16 @@ static void dump_packet_proc(struct media_packet *mp, const str *s) {
if (stream->recording.u.proc.stream_idx == UNINIT_IDX)
return;
struct rtpengine_message *remsg;
unsigned char pkt[sizeof(*remsg) + s->len + MAX_PACKET_HEADER_LEN];
remsg = (void *) pkt;
struct rtpengine_command_packet *cmd;
unsigned char pkt[sizeof(*cmd) + s->len + MAX_PACKET_HEADER_LEN];
cmd = (void *) pkt;
ZERO(*remsg);
remsg->cmd = REMG_PACKET;
cmd->cmd = REMG_PACKET;
//remsg->u.packet.call_idx = stream->call->recording->u.proc.call_idx; // unused
remsg->u.packet.stream_idx = stream->recording.u.proc.stream_idx;
cmd->packet.stream_idx = stream->recording.u.proc.stream_idx;
unsigned int pkt_len = fake_ip_header(remsg->data, mp, s);
pkt_len += sizeof(*remsg);
unsigned int pkt_len = fake_ip_header(cmd->packet.data, mp, s);
pkt_len += sizeof(*cmd);
int ret = write(kernel.fd, pkt, pkt_len);
if (ret < 0)

@ -1818,12 +1818,14 @@ static struct re_dest_addr *find_dest_addr(const struct re_dest_addr_hash *h, co
static int table_get_target_stats(struct rtpengine_table *t, struct rtpengine_stats_info *i, int reset) {
static int table_get_target_stats(struct rtpengine_table *t, const struct re_address *local,
struct rtpengine_stats_info *i, int reset)
{
struct rtpengine_target *g;
unsigned int u;
unsigned long flags;
g = get_target(t, &i->local);
g = get_target(t, local);
if (!g)
return -ENOENT;
@ -2807,7 +2809,7 @@ static struct re_stream *get_stream(struct re_call *call, unsigned int idx) {
ret = streams.array[idx];
if (!ret)
return NULL;
if (call && ret->info.call_idx != call->info.call_idx)
if (call && ret->info.idx.call_idx != call->info.call_idx)
return NULL;
return ret;
}
@ -3033,7 +3035,7 @@ static int table_new_stream(struct rtpengine_table *table, struct rtpengine_stre
/* get call object */
call = get_call_lock(table, info->call_idx);
call = get_call_lock(table, info->idx.call_idx);
if (!call)
return -ENOENT;
@ -3054,7 +3056,7 @@ static int table_new_stream(struct rtpengine_table *table, struct rtpengine_stre
/* check for name collisions */
stream->hash_bucket = crc32_le(0x52342 ^ info->call_idx, info->stream_name, strlen(info->stream_name));
stream->hash_bucket = crc32_le(0x52342 ^ info->idx.call_idx, info->stream_name, strlen(info->stream_name));
stream->hash_bucket = stream->hash_bucket & ((1 << RE_HASH_BITS) - 1);
spin_lock_irqsave(&table->streams_hash_lock[stream->hash_bucket], flags);
@ -3065,7 +3067,7 @@ static int table_new_stream(struct rtpengine_table *table, struct rtpengine_stre
#else
hlist_for_each_entry(hash_entry, &table->streams_hash[stream->hash_bucket], streams_hash_entry) {
#endif
if (hash_entry->info.call_idx == info->call_idx
if (hash_entry->info.idx.call_idx == info->idx.call_idx
&& !strcmp(hash_entry->info.stream_name, info->stream_name))
goto found;
}
@ -3092,7 +3094,7 @@ not_found:
/* copy info */
info->stream_idx = idx;
info->idx.stream_idx = idx;
memcpy(&stream->info, info, sizeof(call->info));
if (!stream->info.max_packets)
stream->info.max_packets = stream_packets_list_limit;
@ -3107,7 +3109,7 @@ not_found:
/* proc_ functions may sleep, so this must be done outside of the lock */
pde = stream->file = proc_create_user(info->stream_name, S_IFREG | S_IRUSR | S_IRGRP, call->root,
&proc_stream_ops, (void *) (unsigned long) info->stream_idx);
&proc_stream_ops, (void *) (unsigned long) info->idx.stream_idx);
err = -ENOMEM;
if (!pde)
goto fail5;
@ -3188,8 +3190,8 @@ static void del_stream(struct re_stream *stream, struct rtpengine_table *table)
DBG("clearing stream's stream_idx entry\n");
_w_lock(&streams.lock, flags);
if (streams.array[stream->info.stream_idx] == stream) {
auto_array_clear_index(&streams, stream->info.stream_idx);
if (streams.array[stream->info.idx.stream_idx] == stream) {
auto_array_clear_index(&streams, stream->info.idx.stream_idx);
stream_put(stream); /* down to 1 ref */
}
else
@ -3200,7 +3202,7 @@ static void del_stream(struct re_stream *stream, struct rtpengine_table *table)
stream_put(stream);
}
static int table_del_stream(struct rtpengine_table *table, const struct rtpengine_stream_info *info) {
static int table_del_stream(struct rtpengine_table *table, const struct rtpengine_stream_idx_info *info) {
int err;
struct re_call *call;
struct re_stream *stream;
@ -3462,12 +3464,11 @@ err:
return;
}
static int stream_packet(struct rtpengine_table *t, const struct rtpengine_packet_info *info,
const unsigned char *data, unsigned int len)
{
static int stream_packet(struct rtpengine_table *t, const struct rtpengine_packet_info *info, size_t len) {
struct re_stream *stream;
int err;
struct re_stream_packet *packet;
const char *data = info->data;
if (!len) /* can't have empty packets */
return -EINVAL;
@ -3507,6 +3508,37 @@ out:
static const size_t min_req_sizes[__REMG_LAST] = {
[REMG_NOOP] = sizeof(struct rtpengine_command_noop),
[REMG_ADD_TARGET] = sizeof(struct rtpengine_command_add_target),
[REMG_DEL_TARGET] = sizeof(struct rtpengine_command_del_target),
[REMG_ADD_DESTINATION] = sizeof(struct rtpengine_command_destination),
[REMG_ADD_CALL] = sizeof(struct rtpengine_command_add_call),
[REMG_DEL_CALL] = sizeof(struct rtpengine_command_del_call),
[REMG_ADD_STREAM] = sizeof(struct rtpengine_command_add_stream),
[REMG_DEL_STREAM] = sizeof(struct rtpengine_command_del_stream),
[REMG_PACKET] = sizeof(struct rtpengine_command_packet),
[REMG_GET_STATS] = sizeof(struct rtpengine_command_stats),
[REMG_GET_RESET_STATS] = sizeof(struct rtpengine_command_stats),
};
static const size_t max_req_sizes[__REMG_LAST] = {
[REMG_NOOP] = sizeof(struct rtpengine_command_noop),
[REMG_ADD_TARGET] = sizeof(struct rtpengine_command_add_target),
[REMG_DEL_TARGET] = sizeof(struct rtpengine_command_del_target),
[REMG_ADD_DESTINATION] = sizeof(struct rtpengine_command_destination),
[REMG_ADD_CALL] = sizeof(struct rtpengine_command_add_call),
[REMG_DEL_CALL] = sizeof(struct rtpengine_command_del_call),
[REMG_ADD_STREAM] = sizeof(struct rtpengine_command_add_stream),
[REMG_DEL_STREAM] = sizeof(struct rtpengine_command_del_stream),
[REMG_PACKET] = sizeof(struct rtpengine_command_packet) + 65535,
[REMG_GET_STATS] = sizeof(struct rtpengine_command_stats),
[REMG_GET_RESET_STATS] = sizeof(struct rtpengine_command_stats),
};
static const size_t input_req_sizes[__REMG_LAST] = {
[REMG_GET_STATS] = sizeof(struct rtpengine_command_stats) - sizeof(struct rtpengine_stats_info),
[REMG_GET_RESET_STATS] = sizeof(struct rtpengine_command_stats) - sizeof(struct rtpengine_stats_info),
};
static inline ssize_t proc_control_read_write(struct file *file, char __user *ubuf, size_t buflen,
int writeable)
@ -3514,95 +3546,130 @@ static inline ssize_t proc_control_read_write(struct file *file, char __user *ub
struct inode *inode;
uint32_t id;
struct rtpengine_table *t;
struct rtpengine_message msgbuf;
struct rtpengine_message *msg;
int err;
if (buflen < sizeof(*msg))
enum rtpengine_command cmd;
char scratchbuf[512];
size_t readlen;
union {
struct rtpengine_command_noop *noop;
struct rtpengine_command_add_target *add_target;
struct rtpengine_command_del_target *del_target;
struct rtpengine_command_destination *destination;
struct rtpengine_command_add_call *add_call;
struct rtpengine_command_del_call *del_call;
struct rtpengine_command_add_stream *add_stream;
struct rtpengine_command_del_stream *del_stream;
struct rtpengine_command_packet *packet;
struct rtpengine_command_stats *stats;
char *storage;
} msg;
// verify absolute minimum size
if (buflen < sizeof(cmd))
return -EIO;
if (buflen == sizeof(*msg))
msg = &msgbuf;
else { /* > */
msg = kmalloc(buflen, GFP_KERNEL);
if (!msg)
// copy request header
if (copy_from_user(&cmd, ubuf, sizeof(cmd)))
return -EFAULT;
// verify request
if (cmd < 1 || cmd >= __REMG_LAST) {
printk(KERN_WARNING "xt_RTPENGINE unimplemented op %u\n", cmd);
return -EINVAL;
}
// verify request size
if (buflen < min_req_sizes[cmd])
return -EMSGSIZE;
if (buflen > max_req_sizes[cmd])
return -ERANGE;
// do we need an extra large storage buffer?
if (buflen > sizeof(scratchbuf)) {
msg.storage = kmalloc(buflen, GFP_KERNEL);
if (!msg.storage)
return -ENOMEM;
}
else
msg.storage = scratchbuf;
// get our table
inode = file->f_path.dentry->d_inode;
id = (uint32_t) (unsigned long) PDE_DATA(inode);
t = get_table(id);
err = -ENOENT;
if (!t)
goto out;
goto err_free;
// copy in the entire request
readlen = input_req_sizes[cmd];
if (!readlen)
readlen = buflen;
err = -EFAULT;
if (copy_from_user(msg, ubuf, buflen))
goto err;
if (copy_from_user(msg.storage, ubuf, readlen))
goto err_table_free;
// execute command
err = 0;
switch (msg->cmd) {
switch (cmd) {
case REMG_NOOP:
if (msg->u.noop.size != sizeof(*msg))
err = -EMSGSIZE;
if (msg->u.noop.last_cmd != __REMG_LAST)
if (msg.noop->noop.last_cmd != __REMG_LAST)
err = -ERANGE;
break;
case REMG_ADD_TARGET:
err = table_new_target(t, &msg->u.target);
err = table_new_target(t, &msg.add_target->target);
break;
case REMG_DEL_TARGET:
err = table_del_target(t, &msg->u.target.local);
err = table_del_target(t, &msg.del_target->local);
break;
case REMG_ADD_DESTINATION:
err = table_add_destination(t, &msg->u.destination);
err = table_add_destination(t, &msg.destination->destination);
break;
case REMG_GET_STATS:
err = -EINVAL;
if (!writeable)
goto err;
err = table_get_target_stats(t, &msg->u.stats, 0);
if (writeable)
err = table_get_target_stats(t, &msg.stats->local, &msg.stats->stats, 0);
break;
case REMG_GET_RESET_STATS:
err = -EINVAL;
if (!writeable)
goto err;
err = table_get_target_stats(t, &msg->u.stats, 1);
if (writeable)
err = table_get_target_stats(t, &msg.stats->local, &msg.stats->stats, 1);
break;
case REMG_ADD_CALL:
err = -EINVAL;
if (!writeable)
goto err;
err = table_new_call(t, &msg->u.call);
if (writeable)
err = table_new_call(t, &msg.add_call->call);
break;
case REMG_DEL_CALL:
err = table_del_call(t, msg->u.call.call_idx);
err = table_del_call(t, msg.del_call->call_idx);
break;
case REMG_ADD_STREAM:
err = -EINVAL;
if (!writeable)
goto err;
err = table_new_stream(t, &msg->u.stream);
if (writeable)
err = table_new_stream(t, &msg.add_stream->stream);
break;
case REMG_DEL_STREAM:
err = table_del_stream(t, &msg->u.stream);
err = table_del_stream(t, &msg.del_stream->stream);
break;
case REMG_PACKET:
err = stream_packet(t, &msg->u.packet, msg->data, buflen - sizeof(*msg));
err = stream_packet(t, &msg.packet->packet, buflen - sizeof(*msg.packet));
break;
default:
printk(KERN_WARNING "xt_RTPENGINE unimplemented op %u\n", msg->cmd);
printk(KERN_WARNING "xt_RTPENGINE unimplemented op %u\n", cmd);
err = -EINVAL;
break;
}
@ -3610,24 +3677,24 @@ static inline ssize_t proc_control_read_write(struct file *file, char __user *ub
table_put(t);
if (err)
goto out;
goto err_free;
if (writeable) {
err = -EFAULT;
if (copy_to_user(ubuf, msg, sizeof(*msg)))
goto out;
if (copy_to_user(ubuf, msg.storage, buflen))
goto err_free;
}
if (msg != &msgbuf)
kfree(msg);
if (msg.storage != scratchbuf)
kfree(msg.storage);
return buflen;
err:
err_table_free:
table_put(t);
out:
if (msg != &msgbuf)
kfree(msg);
err_free:
if (msg.storage != scratchbuf)
kfree(msg.storage);
return err;
}
static ssize_t proc_control_write(struct file *file, const char __user *ubuf, size_t buflen, loff_t *off) {

@ -152,9 +152,13 @@ struct rtpengine_call_info {
char call_id[256];
};
struct rtpengine_stream_info {
struct rtpengine_stream_idx_info {
unsigned int call_idx;
unsigned int stream_idx;
};
struct rtpengine_stream_info {
struct rtpengine_stream_idx_info idx;
unsigned int max_packets;
char stream_name[256];
};
@ -162,60 +166,83 @@ struct rtpengine_stream_info {
struct rtpengine_packet_info {
unsigned int call_idx;
unsigned int stream_idx;
unsigned char data[];
};
struct rtpengine_stats_info {
struct re_address local; // input
uint32_t ssrc[RTPE_NUM_SSRC_TRACKING]; // output
struct rtpengine_ssrc_stats ssrc_stats[RTPE_NUM_SSRC_TRACKING]; // output
uint32_t ssrc[RTPE_NUM_SSRC_TRACKING];
struct rtpengine_ssrc_stats ssrc_stats[RTPE_NUM_SSRC_TRACKING];
};
struct rtpengine_noop_info {
size_t size;
int last_cmd;
};
struct rtpengine_message {
enum {
/* noop_info: */
enum rtpengine_command {
REMG_NOOP = 1,
/* target_info: */
REMG_ADD_TARGET,
REMG_DEL_TARGET,
/* destination_info: */
REMG_ADD_DESTINATION,
/* call_info: */
REMG_ADD_CALL,
REMG_DEL_CALL,
/* stream_info: */
REMG_ADD_STREAM,
REMG_DEL_STREAM,
/* packet_info: */
REMG_PACKET,
/* stats_info: */
REMG_GET_STATS,
REMG_GET_RESET_STATS,
__REMG_LAST
} cmd;
};
union {
struct rtpengine_command_noop {
enum rtpengine_command cmd;
struct rtpengine_noop_info noop;
};
struct rtpengine_command_add_target {
enum rtpengine_command cmd;
struct rtpengine_target_info target;
};
struct rtpengine_command_del_target {
enum rtpengine_command cmd;
struct re_address local;
};
struct rtpengine_command_destination {
enum rtpengine_command cmd;
struct rtpengine_destination_info destination;
};
struct rtpengine_command_add_call {
enum rtpengine_command cmd;
struct rtpengine_call_info call;
};
struct rtpengine_command_del_call {
enum rtpengine_command cmd;
unsigned int call_idx;
};
struct rtpengine_command_add_stream {
enum rtpengine_command cmd;
struct rtpengine_stream_info stream;
};
struct rtpengine_command_del_stream {
enum rtpengine_command cmd;
struct rtpengine_stream_idx_info stream;
};
struct rtpengine_command_packet {
enum rtpengine_command cmd;
struct rtpengine_packet_info packet;
struct rtpengine_stats_info stats;
} u;
};
unsigned char data[];
struct rtpengine_command_stats {
enum rtpengine_command cmd;
struct re_address local; // input
struct rtpengine_stats_info stats; // output
};
struct rtpengine_list_entry {

Loading…
Cancel
Save