|
|
|
@ -285,6 +285,10 @@ struct rtpengine_rtp_stats_a { |
|
|
|
atomic64_t packets; |
|
|
|
atomic64_t bytes; |
|
|
|
}; |
|
|
|
struct rtpengine_output { |
|
|
|
struct rtpengine_output_info output; |
|
|
|
struct re_crypto_context encrypt; |
|
|
|
}; |
|
|
|
struct rtpengine_target { |
|
|
|
atomic_t refcnt; |
|
|
|
uint32_t table; |
|
|
|
@ -296,7 +300,10 @@ struct rtpengine_target { |
|
|
|
struct rtpengine_ssrc_stats ssrc_stats; |
|
|
|
|
|
|
|
struct re_crypto_context decrypt; |
|
|
|
struct re_crypto_context encrypt; |
|
|
|
|
|
|
|
rwlock_t outputs_lock; |
|
|
|
struct rtpengine_output *outputs; |
|
|
|
unsigned int outputs_unfilled; // only ever decreases |
|
|
|
}; |
|
|
|
|
|
|
|
struct re_bitfield { |
|
|
|
@ -853,6 +860,8 @@ static void free_crypto_context(struct re_crypto_context *c) { |
|
|
|
} |
|
|
|
|
|
|
|
static void target_put(struct rtpengine_target *t) { |
|
|
|
unsigned int i; |
|
|
|
|
|
|
|
if (!t) |
|
|
|
return; |
|
|
|
|
|
|
|
@ -862,8 +871,12 @@ static void target_put(struct rtpengine_target *t) { |
|
|
|
DBG("Freeing target\n"); |
|
|
|
|
|
|
|
free_crypto_context(&t->decrypt); |
|
|
|
free_crypto_context(&t->encrypt); |
|
|
|
|
|
|
|
if (t->outputs) { |
|
|
|
for (i = 0; i < t->target.num_destinations; i++) |
|
|
|
free_crypto_context(&t->outputs[i].encrypt); |
|
|
|
kfree(t->outputs); |
|
|
|
} |
|
|
|
kfree(t); |
|
|
|
} |
|
|
|
|
|
|
|
@ -1364,7 +1377,8 @@ static ssize_t proc_blist_read(struct file *f, char __user *b, size_t l, loff_t |
|
|
|
uint32_t id; |
|
|
|
struct rtpengine_table *t; |
|
|
|
struct rtpengine_list_entry *opp; |
|
|
|
int err, port, addr_bucket, i; |
|
|
|
int err, port, addr_bucket; |
|
|
|
unsigned int i; |
|
|
|
struct rtpengine_target *g; |
|
|
|
unsigned long flags; |
|
|
|
|
|
|
|
@ -1409,9 +1423,18 @@ static ssize_t proc_blist_read(struct file *f, char __user *b, size_t l, loff_t |
|
|
|
opp->target.decrypt.last_index = g->target.decrypt.last_index; |
|
|
|
spin_unlock_irqrestore(&g->decrypt.lock, flags); |
|
|
|
|
|
|
|
spin_lock_irqsave(&g->encrypt.lock, flags); |
|
|
|
opp->target.encrypt.last_index = g->target.encrypt.last_index; |
|
|
|
spin_unlock_irqrestore(&g->encrypt.lock, flags); |
|
|
|
_r_lock(&g->outputs_lock, flags); |
|
|
|
if (!g->outputs_unfilled) { |
|
|
|
_r_unlock(&g->outputs_lock, flags); |
|
|
|
for (i = 0; i < g->target.num_destinations; i++) { |
|
|
|
struct rtpengine_output *o = &g->outputs[i]; |
|
|
|
spin_lock_irqsave(&o->encrypt.lock, flags); |
|
|
|
opp->outputs[i] = o->output; |
|
|
|
spin_unlock_irqrestore(&o->encrypt.lock, flags); |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
_r_unlock(&g->outputs_lock, flags); |
|
|
|
|
|
|
|
target_put(g); |
|
|
|
|
|
|
|
@ -1568,24 +1591,30 @@ static void proc_list_crypto_print(struct seq_file *f, struct re_crypto_context |
|
|
|
} |
|
|
|
if (c->hmac && c->hmac->id != REH_NULL) { |
|
|
|
if (!hdr++) |
|
|
|
seq_printf(f, " SRTP %s parameters:\n", label); |
|
|
|
seq_printf(f, " HMAC: %s\n", c->hmac->name ? : "<invalid>"); |
|
|
|
seq_printf(f, " SRTP %s parameters:\n", label); |
|
|
|
seq_printf(f, " HMAC: %s\n", c->hmac->name ? : "<invalid>"); |
|
|
|
seq_printf(f, " auth tag length: %u\n", s->auth_tag_len); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static int proc_list_show(struct seq_file *f, void *v) { |
|
|
|
struct rtpengine_target *g = v; |
|
|
|
int i; |
|
|
|
unsigned int i; |
|
|
|
unsigned long flags; |
|
|
|
|
|
|
|
seq_printf(f, "local "); |
|
|
|
seq_addr_print(f, &g->target.local); |
|
|
|
seq_printf(f, "\n"); |
|
|
|
if (!g->target.non_forwarding) { |
|
|
|
proc_list_addr_print(f, "src", &g->target.src_addr); |
|
|
|
proc_list_addr_print(f, "dst", &g->target.dst_addr); |
|
|
|
|
|
|
|
// all outputs filled? |
|
|
|
_r_lock(&g->outputs_lock, flags); |
|
|
|
if (g->outputs_unfilled) { |
|
|
|
seq_printf(f, " outputs not fully filled (%u missing)\n", g->outputs_unfilled); |
|
|
|
_r_unlock(&g->outputs_lock, flags); |
|
|
|
goto out; |
|
|
|
} |
|
|
|
proc_list_addr_print(f, "mirror", &g->target.mirror_addr); |
|
|
|
_r_unlock(&g->outputs_lock, flags); |
|
|
|
|
|
|
|
proc_list_addr_print(f, "expect", &g->target.expected_src); |
|
|
|
if (g->target.src_mismatch > 0 && g->target.src_mismatch <= ARRAY_SIZE(re_msm_strings)) |
|
|
|
seq_printf(f, " src mismatch action: %s\n", re_msm_strings[g->target.src_mismatch]); |
|
|
|
@ -1599,11 +1628,8 @@ static int proc_list_show(struct seq_file *f, void *v) { |
|
|
|
(unsigned long long) atomic64_read(&g->rtp_stats[i].bytes), |
|
|
|
(unsigned long long) atomic64_read(&g->rtp_stats[i].packets)); |
|
|
|
if (g->target.ssrc) |
|
|
|
seq_printf(f, " SSRC in: %lx\n", (unsigned long) ntohl(g->target.ssrc)); |
|
|
|
if (g->target.ssrc_out) |
|
|
|
seq_printf(f, " SSRC out: %lx\n", (unsigned long) ntohl(g->target.ssrc_out)); |
|
|
|
proc_list_crypto_print(f, &g->decrypt, &g->target.decrypt, "decryption (incoming)"); |
|
|
|
proc_list_crypto_print(f, &g->encrypt, &g->target.encrypt, "encryption (outgoing)"); |
|
|
|
seq_printf(f, " SSRC in: %lx\n", (unsigned long) ntohl(g->target.ssrc)); |
|
|
|
proc_list_crypto_print(f, &g->decrypt, &g->target.decrypt, "decryption"); |
|
|
|
if (g->target.rtcp_mux) |
|
|
|
seq_printf(f, " option: rtcp-mux\n"); |
|
|
|
if (g->target.dtls) |
|
|
|
@ -1619,8 +1645,18 @@ static int proc_list_show(struct seq_file *f, void *v) { |
|
|
|
if (g->target.rtp_stats) |
|
|
|
seq_printf(f, " option: RTP stats\n"); |
|
|
|
|
|
|
|
target_put(g); |
|
|
|
for (i = 0; i < g->target.num_destinations; i++) { |
|
|
|
struct rtpengine_output *o = &g->outputs[i]; |
|
|
|
seq_printf(f, " output #%u\n", i); |
|
|
|
proc_list_addr_print(f, "src", &o->output.src_addr); |
|
|
|
proc_list_addr_print(f, "dst", &o->output.dst_addr); |
|
|
|
if (o->output.ssrc_out) |
|
|
|
seq_printf(f, " SSRC out: %lx\n", (unsigned long) ntohl(o->output.ssrc_out)); |
|
|
|
proc_list_crypto_print(f, &o->encrypt, &o->output.encrypt, "encryption"); |
|
|
|
} |
|
|
|
|
|
|
|
out: |
|
|
|
target_put(g); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
@ -2200,24 +2236,18 @@ static int table_new_target(struct rtpengine_table *t, struct rtpengine_target_i |
|
|
|
|
|
|
|
if (!is_valid_address(&i->local)) |
|
|
|
return -EINVAL; |
|
|
|
if (i->num_destinations > MAX_FORWARD_DESTINATIONS) |
|
|
|
return -EINVAL; |
|
|
|
if (!i->non_forwarding) { |
|
|
|
if (!is_valid_address(&i->src_addr)) |
|
|
|
return -EINVAL; |
|
|
|
if (!is_valid_address(&i->dst_addr)) |
|
|
|
return -EINVAL; |
|
|
|
if (i->src_addr.family != i->dst_addr.family) |
|
|
|
if (!i->num_destinations) |
|
|
|
return -EINVAL; |
|
|
|
} |
|
|
|
if (i->mirror_addr.family) { |
|
|
|
if (!is_valid_address(&i->mirror_addr)) |
|
|
|
return -EINVAL; |
|
|
|
if (i->mirror_addr.family != i->src_addr.family) |
|
|
|
else { |
|
|
|
if (i->num_destinations) |
|
|
|
return -EINVAL; |
|
|
|
} |
|
|
|
if (validate_srtp(&i->decrypt)) |
|
|
|
return -EINVAL; |
|
|
|
if (validate_srtp(&i->encrypt)) |
|
|
|
return -EINVAL; |
|
|
|
|
|
|
|
DBG("Creating new target\n"); |
|
|
|
|
|
|
|
@ -2231,17 +2261,21 @@ static int table_new_target(struct rtpengine_table *t, struct rtpengine_target_i |
|
|
|
g->table = t->id; |
|
|
|
atomic_set(&g->refcnt, 1); |
|
|
|
spin_lock_init(&g->decrypt.lock); |
|
|
|
spin_lock_init(&g->encrypt.lock); |
|
|
|
memcpy(&g->target, i, sizeof(*i)); |
|
|
|
crypto_context_init(&g->decrypt, &g->target.decrypt); |
|
|
|
crypto_context_init(&g->encrypt, &g->target.encrypt); |
|
|
|
spin_lock_init(&g->ssrc_stats_lock); |
|
|
|
g->ssrc_stats.lost_bits = -1; |
|
|
|
rwlock_init(&g->outputs_lock); |
|
|
|
|
|
|
|
if (i->num_destinations) { |
|
|
|
err = -ENOMEM; |
|
|
|
g->outputs = kzalloc(sizeof(*g->outputs) * i->num_destinations, GFP_KERNEL); |
|
|
|
if (!g->outputs) |
|
|
|
goto fail2; |
|
|
|
g->outputs_unfilled = i->num_destinations; |
|
|
|
} |
|
|
|
|
|
|
|
err = gen_session_keys(&g->decrypt, &g->target.decrypt); |
|
|
|
if (err) |
|
|
|
goto fail2; |
|
|
|
err = gen_session_keys(&g->encrypt, &g->target.encrypt); |
|
|
|
if (err) |
|
|
|
goto fail2; |
|
|
|
|
|
|
|
@ -2335,11 +2369,67 @@ fail4: |
|
|
|
if (ba) |
|
|
|
kfree(ba); |
|
|
|
fail2: |
|
|
|
if (g->outputs) |
|
|
|
kfree(g->outputs); |
|
|
|
kfree(g); |
|
|
|
fail1: |
|
|
|
return err; |
|
|
|
} |
|
|
|
|
|
|
|
static int table_add_destination(struct rtpengine_table *t, struct rtpengine_destination_info *i) { |
|
|
|
unsigned long flags; |
|
|
|
int err; |
|
|
|
struct rtpengine_target *g; |
|
|
|
|
|
|
|
// validate input |
|
|
|
|
|
|
|
if (!is_valid_address(&i->output.src_addr)) |
|
|
|
return -EINVAL; |
|
|
|
if (!is_valid_address(&i->output.dst_addr)) |
|
|
|
return -EINVAL; |
|
|
|
if (i->output.src_addr.family != i->output.dst_addr.family) |
|
|
|
return -EINVAL; |
|
|
|
if (validate_srtp(&i->output.encrypt)) |
|
|
|
return -EINVAL; |
|
|
|
|
|
|
|
g = get_target(t, &i->local); |
|
|
|
if (!g) |
|
|
|
return -ENOENT; |
|
|
|
|
|
|
|
// ready to fill in |
|
|
|
|
|
|
|
_w_lock(&g->outputs_lock, flags); |
|
|
|
|
|
|
|
if (!g->outputs_unfilled) |
|
|
|
panic("BUG num of unfilled outputs %u", g->outputs_unfilled); |
|
|
|
|
|
|
|
// out of range entry? |
|
|
|
err = -ERANGE; |
|
|
|
if (i->num >= g->target.num_destinations) |
|
|
|
goto out; |
|
|
|
|
|
|
|
// already filled? |
|
|
|
err = -EEXIST; |
|
|
|
if (g->outputs[i->num].output.src_addr.family) |
|
|
|
goto out; |
|
|
|
|
|
|
|
spin_lock_init(&g->outputs[i->num].encrypt.lock); |
|
|
|
crypto_context_init(&g->outputs[i->num].encrypt, &i->output.encrypt); |
|
|
|
err = gen_session_keys(&g->outputs[i->num].encrypt, &i->output.encrypt); |
|
|
|
if (err) |
|
|
|
goto out; |
|
|
|
|
|
|
|
g->outputs[i->num].output = i->output; |
|
|
|
g->outputs_unfilled--; |
|
|
|
|
|
|
|
err = 0; |
|
|
|
|
|
|
|
out: |
|
|
|
_w_unlock(&g->outputs_lock, flags); |
|
|
|
target_put(g); |
|
|
|
return err; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -3312,16 +3402,16 @@ static inline ssize_t proc_control_read_write(struct file *file, char __user *ub |
|
|
|
err = -ERANGE; |
|
|
|
break; |
|
|
|
|
|
|
|
case REMG_ADD: |
|
|
|
case REMG_ADD_TARGET: |
|
|
|
err = table_new_target(t, &msg->u.target); |
|
|
|
break; |
|
|
|
|
|
|
|
case REMG_DEL: |
|
|
|
case REMG_DEL_TARGET: |
|
|
|
err = table_del_target(t, &msg->u.target.local); |
|
|
|
break; |
|
|
|
|
|
|
|
case REMG_UPDATE: |
|
|
|
err = -EOPNOTSUPP; |
|
|
|
case REMG_ADD_DESTINATION: |
|
|
|
err = table_add_destination(t, &msg->u.destination); |
|
|
|
break; |
|
|
|
|
|
|
|
case REMG_GET_STATS: |
|
|
|
@ -4220,11 +4310,14 @@ static unsigned int rtpengine46(struct sk_buff *skb, struct rtpengine_table *t, |
|
|
|
int rtp_pt_idx = -2; |
|
|
|
unsigned int datalen, pllen; |
|
|
|
uint32_t *u32; |
|
|
|
struct rtp_parsed rtp; |
|
|
|
struct rtp_parsed rtp, rtp2; |
|
|
|
ssize_t offset; |
|
|
|
uint64_t pkt_idx; |
|
|
|
struct re_stream *stream; |
|
|
|
struct re_stream_packet *packet; |
|
|
|
const char *errstr = NULL; |
|
|
|
unsigned long flags; |
|
|
|
unsigned int i; |
|
|
|
|
|
|
|
#if (RE_HAS_MEASUREDELAY) |
|
|
|
uint64_t starttime, endtime, delay; |
|
|
|
@ -4248,6 +4341,15 @@ static unsigned int rtpengine46(struct sk_buff *skb, struct rtpengine_table *t, |
|
|
|
if (!g) |
|
|
|
goto skip2; |
|
|
|
|
|
|
|
// all our outputs filled? |
|
|
|
_r_lock(&g->outputs_lock, flags); |
|
|
|
if (g->outputs_unfilled) { |
|
|
|
// pass to application |
|
|
|
_r_unlock(&g->outputs_lock, flags); |
|
|
|
goto skip1; |
|
|
|
} |
|
|
|
_r_unlock(&g->outputs_lock, flags); |
|
|
|
|
|
|
|
DBG("target found, src "MIPF" -> dst "MIPF"\n", MIPP(g->target.src_addr), MIPP(g->target.dst_addr)); |
|
|
|
DBG("target decrypt hmac and cipher are %s and %s", g->decrypt.hmac->name, |
|
|
|
g->decrypt.cipher->name); |
|
|
|
@ -4338,15 +4440,6 @@ src_check_ok: |
|
|
|
rtp.payload[16], rtp.payload[17], rtp.payload[18], rtp.payload[19]); |
|
|
|
|
|
|
|
not_rtp: |
|
|
|
if (g->target.mirror_addr.family) { |
|
|
|
DBG("sending mirror packet to dst "MIPF"\n", MIPP(g->target.mirror_addr)); |
|
|
|
skb2 = skb_copy_expand(skb, MAX_HEADER, MAX_SKB_TAIL_ROOM, GFP_ATOMIC); |
|
|
|
err = send_proxy_packet(skb2, &g->target.src_addr, &g->target.mirror_addr, g->target.tos, |
|
|
|
par); |
|
|
|
if (err) |
|
|
|
atomic64_inc(&g->stats.errors); |
|
|
|
} |
|
|
|
|
|
|
|
if (g->target.do_intercept) { |
|
|
|
DBG("do_intercept is set\n"); |
|
|
|
stream = get_stream_lock(NULL, g->target.intercept_stream_idx); |
|
|
|
@ -4368,29 +4461,49 @@ intercept_done: |
|
|
|
} |
|
|
|
|
|
|
|
no_intercept: |
|
|
|
if (rtp.ok) { |
|
|
|
// SSRC substitution |
|
|
|
if (g->target.transcoding && g->target.ssrc_out) |
|
|
|
rtp.header->ssrc = g->target.ssrc_out; |
|
|
|
|
|
|
|
pkt_idx = packet_index(&g->encrypt, &g->target.encrypt, rtp.header); |
|
|
|
pllen = rtp.payload_len; |
|
|
|
srtp_encrypt(&g->encrypt, &g->target.encrypt, &rtp, pkt_idx); |
|
|
|
srtp_authenticate(&g->encrypt, &g->target.encrypt, &rtp, pkt_idx); |
|
|
|
skb_put(skb, rtp.payload_len - pllen); |
|
|
|
} |
|
|
|
// output |
|
|
|
for (i = 0; i < g->target.num_destinations; i++) { |
|
|
|
struct rtpengine_output *o = &g->outputs[i]; |
|
|
|
// do we need a copy? |
|
|
|
if (i == (g->target.num_destinations - 1)) |
|
|
|
skb2 = skb; // last iteration - use original |
|
|
|
else { |
|
|
|
// make copy |
|
|
|
skb2 = skb_copy_expand(skb, MAX_HEADER, MAX_SKB_TAIL_ROOM, GFP_ATOMIC); |
|
|
|
if (!skb2) { |
|
|
|
log_err("out of memory while creating skb copy"); |
|
|
|
atomic64_inc(&g->stats.errors); |
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
// adjust RTP pointers |
|
|
|
offset = skb2->data - skb->data; |
|
|
|
rtp2 = rtp; |
|
|
|
rtp2.header = (void *) (((char *) rtp2.header) + offset); |
|
|
|
rtp2.payload = (void *) (((char *) rtp2.payload) + offset); |
|
|
|
|
|
|
|
if (rtp2.ok) { |
|
|
|
// SSRC substitution |
|
|
|
if (g->target.transcoding && o->output.ssrc_out) |
|
|
|
rtp2.header->ssrc = o->output.ssrc_out; |
|
|
|
|
|
|
|
pkt_idx = packet_index(&o->encrypt, &o->output.encrypt, rtp2.header); |
|
|
|
pllen = rtp2.payload_len; |
|
|
|
srtp_encrypt(&o->encrypt, &o->output.encrypt, &rtp2, pkt_idx); |
|
|
|
srtp_authenticate(&o->encrypt, &o->output.encrypt, &rtp2, pkt_idx); |
|
|
|
skb_put(skb2, rtp2.payload_len - pllen); |
|
|
|
} |
|
|
|
|
|
|
|
err = send_proxy_packet(skb, &g->target.src_addr, &g->target.dst_addr, g->target.tos, par); |
|
|
|
err = send_proxy_packet(skb2, &o->output.src_addr, &o->output.dst_addr, o->output.tos, par); |
|
|
|
if (err) |
|
|
|
atomic64_inc(&g->stats.errors); |
|
|
|
} |
|
|
|
|
|
|
|
if (atomic64_read(&g->stats.packets)==0) |
|
|
|
atomic_set(&g->stats.in_tos,in_tos); |
|
|
|
|
|
|
|
if (err) |
|
|
|
atomic64_inc(&g->stats.errors); |
|
|
|
else { |
|
|
|
atomic64_inc(&g->stats.packets); |
|
|
|
atomic64_add(datalen, &g->stats.bytes); |
|
|
|
} |
|
|
|
atomic64_inc(&g->stats.packets); |
|
|
|
atomic64_add(datalen, &g->stats.bytes); |
|
|
|
|
|
|
|
if (rtp_pt_idx >= 0) { |
|
|
|
atomic64_inc(&g->rtp_stats[rtp_pt_idx].packets); |
|
|
|
|