Commit 845ddb44 authored by Jon Grimm's avatar Jon Grimm Committed by Sridhar Samudrala

[SCTP] Support v4-mapped-v6 addresses (Ardelle Fan)

parent 33585c7e
......@@ -611,4 +611,23 @@ int static inline __sctp_sstate(const struct sock *sk, sctp_sock_state_t state)
return sk->sk_state == state;
}
/* Map v4-mapped v6 address back to v4 address */
static inline void sctp_v6_map_v4(union sctp_addr *addr)
{
addr->v4.sin_family = AF_INET;
addr->v4.sin_port = addr->v6.sin6_port;
addr->v4.sin_addr.s_addr = addr->v6.sin6_addr.s6_addr32[3];
}
/* Map v4 address to v4-mapped v6 address */
static inline void sctp_v4_map_v6(union sctp_addr *addr)
{
addr->v6.sin6_family = AF_INET6;
addr->v6.sin6_port = addr->v4.sin_port;
addr->v6.sin6_addr.s6_addr32[3] = addr->v4.sin_addr.s_addr;
addr->v6.sin6_addr.s6_addr32[0] = 0;
addr->v6.sin6_addr.s6_addr32[1] = 0;
addr->v6.sin6_addr.s6_addr32[2] = htonl(0x0000ffff);
}
#endif /* __net_sctp_h__ */
......@@ -260,11 +260,13 @@ struct sctp_af {
struct sock *sk);
void (*to_sk_daddr) (union sctp_addr *,
struct sock *sk);
int (*addr_valid) (union sctp_addr *);
int (*addr_valid) (union sctp_addr *,
struct sctp_opt *);
sctp_scope_t (*scope) (union sctp_addr *);
void (*inaddr_any) (union sctp_addr *, unsigned short);
int (*is_any) (const union sctp_addr *);
int (*available) (const union sctp_addr *);
int (*available) (union sctp_addr *,
struct sctp_opt *);
int (*skb_iif) (const struct sk_buff *sk);
int (*is_ce) (const struct sk_buff *sk);
void (*seq_dump_addr)(struct seq_file *seq,
......@@ -282,7 +284,7 @@ int sctp_register_af(struct sctp_af *);
struct sctp_pf {
void (*event_msgname)(struct sctp_ulpevent *, char *, int *);
void (*skb_msgname) (struct sk_buff *, char *, int *);
int (*af_supported) (sa_family_t);
int (*af_supported) (sa_family_t, struct sctp_opt *);
int (*cmp_addr) (const union sctp_addr *,
const union sctp_addr *,
struct sctp_opt *);
......@@ -291,6 +293,7 @@ struct sctp_pf {
int (*supported_addrs)(const struct sctp_opt *, __u16 *);
struct sock *(*create_accept_sk) (struct sock *sk,
struct sctp_association *asoc);
void (*addr_v4map) (struct sctp_opt *, union sctp_addr *);
struct sctp_af *af;
};
......
......@@ -2,6 +2,7 @@
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001-2003 International Business Machines, Corp.
* Copyright (c) 2002 Intel Corp.
*
* This file is part of the SCTP kernel reference Implementation
*
......@@ -41,6 +42,7 @@
* Jon Grimm <jgrimm@us.ibm.com>
* Daisy Chang <daisyc@us.ibm.com>
* Ryan Layer <rmlayer@us.ibm.com>
* Ardelle Fan <ardelle.fan@intel.com>
* Sridhar Samudrala <sri@us.ibm.com>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
......
......@@ -150,7 +150,7 @@ int sctp_rcv(struct sk_buff *skb)
* IP broadcast addresses cannot be used in an SCTP transport
* address."
*/
if (!af->addr_valid(&src) || !af->addr_valid(&dest))
if (!af->addr_valid(&src, NULL) || !af->addr_valid(&dest, NULL))
goto discard_it;
asoc = __sctp_rcv_lookup(skb, &src, &dest, &transport);
......
......@@ -2,6 +2,7 @@
* Copyright (c) 2001 Nokia, Inc.
* Copyright (c) 2001 La Monte H.P. Yarroll
* Copyright (c) 2002-2003 International Business Machines, Corp.
* Copyright (c) 2002-2003 Intel Corp.
*
* This file is part of the SCTP kernel reference Implementation
*
......@@ -15,7 +16,7 @@
*
* The SCTP reference implementation is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied
* ************************
* ************************
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
......@@ -32,14 +33,15 @@
* http://www.sf.net/projects/lksctp
*
* Written or modified by:
* Le Yanqun <yanqun.le@nokia.com>
* Le Yanqun <yanqun.le@nokia.com>
* Hui Huang <hui.huang@nokia.com>
* La Monte H.P. Yarroll <piggy@acm.org>
* Sridhar Samudrala <sri@us.ibm.com>
* Jon Grimm <jgrimm@us.ibm.com>
* Jon Grimm <jgrimm@us.ibm.com>
* Ardelle Fan <ardelle.fan@intel.com>
*
* Based on:
* linux/net/ipv6/tcp_ipv6.c
* linux/net/ipv6/tcp_ipv6.c
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
......@@ -172,7 +174,7 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport,
SCTP_DEBUG_PRINTK("%s: skb:%p, len:%d, "
"src:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x "
"dst:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
__FUNCTION__, skb, skb->len,
__FUNCTION__, skb, skb->len,
NIP6(fl.fl6_src), NIP6(fl.fl6_dst));
SCTP_INC_STATS(SctpOutSCTPPacks);
......@@ -370,13 +372,28 @@ static void sctp_v6_from_sk(union sctp_addr *addr, struct sock *sk)
/* Initialize sk->sk_rcv_saddr from sctp_addr. */
static void sctp_v6_to_sk_saddr(union sctp_addr *addr, struct sock *sk)
{
inet6_sk(sk)->rcv_saddr = addr->v6.sin6_addr;
if (addr->sa.sa_family == AF_INET && sctp_sk(sk)->v4mapped) {
inet6_sk(sk)->rcv_saddr.s6_addr32[0] = 0;
inet6_sk(sk)->rcv_saddr.s6_addr32[1] = 0;
inet6_sk(sk)->rcv_saddr.s6_addr32[2] = htonl(0x0000ffff);
inet6_sk(sk)->rcv_saddr.s6_addr32[3] =
addr->v4.sin_addr.s_addr;
} else {
inet6_sk(sk)->rcv_saddr = addr->v6.sin6_addr;
}
}
/* Initialize sk->sk_daddr from sctp_addr. */
static void sctp_v6_to_sk_daddr(union sctp_addr *addr, struct sock *sk)
{
inet6_sk(sk)->daddr = addr->v6.sin6_addr;
if (addr->sa.sa_family == AF_INET && sctp_sk(sk)->v4mapped) {
inet6_sk(sk)->daddr.s6_addr32[0] = 0;
inet6_sk(sk)->daddr.s6_addr32[1] = 0;
inet6_sk(sk)->daddr.s6_addr32[2] = htonl(0x0000ffff);
inet6_sk(sk)->daddr.s6_addr32[3] = addr->v4.sin_addr.s_addr;
} else {
inet6_sk(sk)->daddr = addr->v6.sin6_addr;
}
}
/* Initialize a sctp_addr from a dst_entry. */
......@@ -390,13 +407,30 @@ static void sctp_v6_dst_saddr(union sctp_addr *addr, struct dst_entry *dst,
}
/* Compare addresses exactly.
* FIXME: v4-mapped-v6.
* v4-mapped-v6 is also in consideration.
*/
static int sctp_v6_cmp_addr(const union sctp_addr *addr1,
const union sctp_addr *addr2)
{
if (addr1->sa.sa_family != addr2->sa.sa_family)
if (addr1->sa.sa_family != addr2->sa.sa_family) {
if (addr1->sa.sa_family == AF_INET &&
addr2->sa.sa_family == AF_INET6 &&
IPV6_ADDR_MAPPED == ipv6_addr_type(&addr2->v6.sin6_addr)) {
if (addr2->v6.sin6_port == addr1->v4.sin_port &&
addr2->v6.sin6_addr.s6_addr32[3] ==
addr1->v4.sin_addr.s_addr)
return 1;
}
if (addr2->sa.sa_family == AF_INET &&
addr1->sa.sa_family == AF_INET6 &&
IPV6_ADDR_MAPPED == ipv6_addr_type(&addr1->v6.sin6_addr)) {
if (addr1->v6.sin6_port == addr2->v4.sin_port &&
addr1->v6.sin6_addr.s6_addr32[3] ==
addr2->v4.sin_addr.s_addr)
return 1;
}
return 0;
}
if (ipv6_addr_cmp(&addr1->v6.sin6_addr, &addr2->v6.sin6_addr))
return 0;
/* If this is a linklocal address, compare the scope_id. */
......@@ -427,7 +461,7 @@ static int sctp_v6_is_any(const union sctp_addr *addr)
}
/* Should this be available for binding? */
static int sctp_v6_available(const union sctp_addr *addr)
static int sctp_v6_available(union sctp_addr *addr, struct sctp_opt *sp)
{
int type;
struct in6_addr *in6 = (struct in6_addr *)&addr->v6.sin6_addr;
......@@ -435,6 +469,12 @@ static int sctp_v6_available(const union sctp_addr *addr)
type = ipv6_addr_type(in6);
if (IPV6_ADDR_ANY == type)
return 1;
if (type == IPV6_ADDR_MAPPED) {
if (sp && !sp->v4mapped)
return 0;
sctp_v6_map_v4(addr);
return sctp_get_af_specific(AF_INET)->available(addr, sp);
}
if (!(type & IPV6_ADDR_UNICAST))
return 0;
......@@ -448,11 +488,20 @@ static int sctp_v6_available(const union sctp_addr *addr)
* Return 0 - If the address is a non-unicast or an illegal address.
* Return 1 - If the address is a unicast.
*/
static int sctp_v6_addr_valid(union sctp_addr *addr)
static int sctp_v6_addr_valid(union sctp_addr *addr, struct sctp_opt *sp)
{
int ret = ipv6_addr_type(&addr->v6.sin6_addr);
/* FIXME: v4-mapped-v6 address support. */
/* Support v4-mapped-v6 address. */
if (ret == IPV6_ADDR_MAPPED) {
/* Note: This routine is used in input, so v4-mapped-v6
* are disallowed here when there is no sctp_opt.
*/
if (!sp || !sp->v4mapped)
return 0;
sctp_v6_map_v4(addr);
return sctp_get_af_specific(AF_INET)->addr_valid(addr, sp);
}
/* Is this a non-unicast address */
if (!(ret & IPV6_ADDR_UNICAST))
......@@ -536,7 +585,7 @@ struct sock *sctp_v6_create_accept_sk(struct sock *sk,
newnp->saddr = np->saddr;
newnp->rcv_saddr = np->rcv_saddr;
newinet->dport = htons(asoc->peer.port);
newnp->daddr = asoc->peer.primary_addr.v6.sin6_addr;
sctp_v6_to_sk_daddr(&asoc->peer.primary_addr, newsk);
/* Init the ipv4 part of the socket since we can have sockets
* using v6 API for ipv4.
......@@ -566,6 +615,13 @@ struct sock *sctp_v6_create_accept_sk(struct sock *sk,
return newsk;
}
/* Map v4 address to mapped v6 address */
static void sctp_v6_addr_v4map(struct sctp_opt *sp, union sctp_addr *addr)
{
if (sp->v4mapped && AF_INET == addr->sa.sa_family)
sctp_v4_map_v6(addr);
}
/* Where did this skb come from? */
static int sctp_v6_skb_iif(const struct sk_buff *skb)
{
......@@ -617,10 +673,11 @@ static void sctp_inet6_event_msgname(struct sctp_ulpevent *event,
*/
/* Map ipv4 address into v4-mapped-on-v6 address. */
if (AF_INET == addr->sa.sa_family) {
/* FIXME: Easy, but there was no way to test this
* yet.
*/
if (sctp_sk(event->asoc->base.sk)->v4mapped &&
AF_INET == addr->sa.sa_family) {
sctp_v4_map_v6((union sctp_addr *)sin6);
sin6->sin6_addr.s6_addr32[3] =
addr->v4.sin_addr.s_addr;
return;
}
......@@ -644,16 +701,15 @@ static void sctp_inet6_skb_msgname(struct sk_buff *skb, char *msgname,
sh = (struct sctphdr *)skb->h.raw;
sin6->sin6_port = sh->source;
/* FIXME: Map ipv4 address into v4-mapped-on-v6 address. */
if (__constant_htons(ETH_P_IP) == skb->protocol) {
/* FIXME: The latest I-D added options for two
* behaviors.
*/
/* Map ipv4 address into v4-mapped-on-v6 address. */
if (sctp_sk(skb->sk)->v4mapped &&
skb->nh.iph->version == 4) {
sctp_v4_map_v6((union sctp_addr *)sin6);
sin6->sin6_addr.s6_addr32[3] = skb->nh.iph->saddr;
return;
}
/* Otherwise, just copy the v6 address. */
ipv6_addr_copy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr);
if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) {
struct sctp_ulpevent *ev = sctp_skb2event(skb);
......@@ -663,16 +719,15 @@ static void sctp_inet6_skb_msgname(struct sk_buff *skb, char *msgname,
}
/* Do we support this AF? */
static int sctp_inet6_af_supported(sa_family_t family)
static int sctp_inet6_af_supported(sa_family_t family, struct sctp_opt *sp)
{
/* FIXME: v4-mapped-v6 addresses. The I-D is still waffling
* on what to do with sockaddr formats for PF_INET6 sockets.
* For now assume we'll support both.
*/
switch (family) {
case AF_INET6:
case AF_INET:
return 1;
/* v4-mapped-v6 addresses */
case AF_INET:
if (sp->v4mapped)
return 1;
default:
return 0;
}
......@@ -730,7 +785,7 @@ static int sctp_inet6_bind_verify(struct sctp_opt *opt, union sctp_addr *addr)
}
af = opt->pf->af;
}
return af->available(addr);
return af->available(addr, opt);
}
/* Verify that the provided sockaddr looks bindable. Common verification,
......@@ -863,6 +918,7 @@ static struct sctp_pf sctp_pf_inet6_specific = {
.send_verify = sctp_inet6_send_verify,
.supported_addrs = sctp_inet6_supported_addrs,
.create_accept_sk = sctp_v6_create_accept_sk,
.addr_v4map = sctp_v6_addr_v4map,
.af = &sctp_ipv6_specific,
};
......
......@@ -340,7 +340,7 @@ static int sctp_v4_is_any(const union sctp_addr *addr)
* Return 0 - If the address is a non-unicast or an illegal address.
* Return 1 - If the address is a unicast.
*/
static int sctp_v4_addr_valid(union sctp_addr *addr)
static int sctp_v4_addr_valid(union sctp_addr *addr, struct sctp_opt *sp)
{
/* Is this a non-unicast address or a unusable SCTP address? */
if (IS_IPV4_UNUSABLE_ADDRESS(&addr->v4.sin_addr.s_addr))
......@@ -350,7 +350,7 @@ static int sctp_v4_addr_valid(union sctp_addr *addr)
}
/* Should this be available for binding? */
static int sctp_v4_available(const union sctp_addr *addr)
static int sctp_v4_available(union sctp_addr *addr, struct sctp_opt *sp)
{
int ret = inet_addr_type(addr->v4.sin_addr.s_addr);
......@@ -580,6 +580,12 @@ struct sock *sctp_v4_create_accept_sk(struct sock *sk,
return newsk;
}
/* Map address, empty for v4 family */
static void sctp_v4_addr_v4map(struct sctp_opt *sp, union sctp_addr *addr)
{
/* Empty */
}
/* Dump the v4 addr to the seq file. */
static void sctp_v4_seq_dump_addr(struct seq_file *seq, union sctp_addr *addr)
{
......@@ -709,7 +715,7 @@ static void sctp_inet_skb_msgname(struct sk_buff *skb, char *msgname, int *len)
}
/* Do we support this AF? */
static int sctp_inet_af_supported(sa_family_t family)
static int sctp_inet_af_supported(sa_family_t family, struct sctp_opt *sp)
{
/* PF_INET only supports AF_INET addresses. */
return (AF_INET == family);
......@@ -737,7 +743,7 @@ static int sctp_inet_cmp_addr(const union sctp_addr *addr1,
*/
static int sctp_inet_bind_verify(struct sctp_opt *opt, union sctp_addr *addr)
{
return sctp_v4_available(addr);
return sctp_v4_available(addr, opt);
}
/* Verify that sockaddr looks sendable. Common verification has already
......@@ -783,6 +789,7 @@ static struct sctp_pf sctp_pf_inet = {
.send_verify = sctp_inet_send_verify,
.supported_addrs = sctp_inet_supported_addrs,
.create_accept_sk = sctp_v4_create_accept_sk,
.addr_v4map = sctp_v4_addr_v4map,
.af = &sctp_ipv4_specific,
};
......
......@@ -156,6 +156,9 @@ struct sctp_transport *sctp_addr_id2transport(struct sock *sk,
if (id_asoc && (id_asoc != addr_asoc))
return NULL;
sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk),
(union sctp_addr *)addr);
return transport;
}
......@@ -206,7 +209,7 @@ static struct sctp_af *sctp_sockaddr_af(struct sctp_opt *opt,
return NULL;
/* Does this PF support this AF? */
if (!opt->pf->af_supported(addr->sa.sa_family))
if (!opt->pf->af_supported(addr->sa.sa_family, opt))
return NULL;
/* If we get this far, af is valid. */
......@@ -1362,7 +1365,7 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk,
/* Free the event which includes releasing the reference to
* the owner of the skb, freeing the skb and updating the
* rwnd.
*/
*/
sctp_ulpevent_free(event);
}
out:
......@@ -1747,14 +1750,18 @@ static int sctp_setsockopt_associnfo(struct sock *sk, char *optval, int optlen)
static int sctp_setsockopt_mappedv4(struct sock *sk, char *optval, int optlen)
{
int val;
struct sctp_opt *sp = sctp_sk(sk);
if (optlen < sizeof(int))
return -EINVAL;
if (get_user(val, (int *)optval))
return -EFAULT;
/* FIXME: Put real support here. */
if (val)
sp->v4mapped = 1;
else
sp->v4mapped = 0;
return -ENOPROTOOPT;
return 0;
}
/*
......@@ -2305,6 +2312,9 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval,
status.sstat_primary.spinfo_assoc_id = sctp_assoc2id(transport->asoc);
memcpy(&status.sstat_primary.spinfo_address,
&(transport->ipaddr), sizeof(union sctp_addr));
/* Map ipv4 address into v4-mapped-on-v6 address. */
sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk),
(union sctp_addr *)&status.sstat_primary.spinfo_address);
status.sstat_primary.spinfo_state = transport->active;
status.sstat_primary.spinfo_cwnd = transport->cwnd;
status.sstat_primary.spinfo_srtt = transport->srtt;
......@@ -2642,6 +2652,8 @@ static int sctp_getsockopt_peer_addrs(struct sock *sk, int len,
struct sctp_getaddrs getaddrs;
struct sctp_transport *from;
struct sockaddr_storage *to;
union sctp_addr temp;
struct sctp_opt *sp = sctp_sk(sk);
if (len != sizeof(struct sctp_getaddrs))
return -EINVAL;
......@@ -2660,7 +2672,9 @@ static int sctp_getsockopt_peer_addrs(struct sock *sk, int len,
to = getaddrs.addrs;
list_for_each(pos, &asoc->peer.transport_addr_list) {
from = list_entry(pos, struct sctp_transport, transports);
if (copy_to_user(to, &from->ipaddr, sizeof(from->ipaddr)))
memcpy(&temp, &from->ipaddr, sizeof(temp));
sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
if (copy_to_user(to, &temp, sizeof(temp)))
return -EFAULT;
to ++;
cnt ++;
......@@ -2722,6 +2736,8 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
struct sctp_getaddrs getaddrs;
struct sctp_sockaddr_entry *from;
struct sockaddr_storage *to;
union sctp_addr temp;
struct sctp_opt *sp = sctp_sk(sk);
if (len != sizeof(struct sctp_getaddrs))
return -EINVAL;
......@@ -2750,7 +2766,9 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
from = list_entry(pos,
struct sctp_sockaddr_entry,
list);
if (copy_to_user(to, &from->a, sizeof(from->a)))
memcpy(&temp, &from->a, sizeof(temp));
sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
if (copy_to_user(to, &temp, sizeof(temp)))
return -EFAULT;
to ++;
cnt ++;
......@@ -2774,6 +2792,7 @@ static int sctp_getsockopt_primary_addr(struct sock *sk, int len,
{
struct sctp_prim prim;
struct sctp_association *asoc;
struct sctp_opt *sp = sctp_sk(sk);
if (len != sizeof(struct sctp_prim))
return -EINVAL;
......@@ -2791,6 +2810,9 @@ static int sctp_getsockopt_primary_addr(struct sock *sk, int len,
memcpy(&prim.ssp_addr, &asoc->peer.primary_path->ipaddr,
sizeof(union sctp_addr));
sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp,
(union sctp_addr *)&prim.ssp_addr);
if (copy_to_user(optval, &prim, sizeof(struct sctp_prim)))
return -EFAULT;
......@@ -2805,6 +2827,8 @@ static int sctp_getsockopt_primary_addr(struct sock *sk, int len,
* specify a default set of parameters that would normally be supplied
* through the inclusion of ancillary data. This socket option allows
* such an application to set the default sctp_sndrcvinfo structure.
* The application that wishes to use this socket option simply passes
* in to this call the sctp_sndrcvinfo structure defined in Section
* 5.2.2) The input parameters accepted by this call include
......@@ -3012,12 +3036,13 @@ static int sctp_getsockopt_mappedv4(struct sock *sk, int len,
char *optval, int *optlen)
{
int val;
struct sctp_opt *sp = sctp_sk(sk);
if (len < sizeof(int))
return -EINVAL;
len = sizeof(int);
/* FIXME: Until we have support, return disabled. */
val = 0;
val = sp->v4mapped;
if (put_user(len, optlen))
return -EFAULT;
if (copy_to_user(optval, &val, len))
......@@ -3863,7 +3888,7 @@ static inline int sctp_verify_addr(struct sock *sk, union sctp_addr *addr,
return -EINVAL;
/* Is this a valid SCTP address? */
if (!af->addr_valid(addr))
if (!af->addr_valid(addr, sctp_sk(sk)))
return -EINVAL;
if (!sctp_sk(sk)->pf->send_verify(sctp_sk(sk), (addr)))
......
......@@ -35,6 +35,7 @@
* Written or modified by:
* Jon Grimm <jgrimm@us.ibm.com>
* La Monte H.P. Yarroll <piggy@acm.org>
* Ardelle Fan <ardelle.fan@intel.com>
* Sridhar Samudrala <sri@us.ibm.com>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
......@@ -286,6 +287,11 @@ struct sctp_ulpevent *sctp_ulpevent_make_peer_addr_change(
*/
memcpy(&spc->spc_aaddr, aaddr, sizeof(struct sockaddr_storage));
/* Map ipv4 address into v4-mapped-on-v6 address. */
sctp_get_pf_specific(asoc->base.sk->sk_family)->addr_v4map(
sctp_sk(asoc->base.sk),
(union sctp_addr *)&spc->spc_aaddr);
return event;
fail:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment