Commit f72c208b authored by David S. Miller's avatar David S. Miller

Merge http://linux-lksctp.bkbits.net/lksctp-2.5

into nuts.ninka.net:/disk1/davem/BK/net-2.5
parents f3e971e4 4a542137
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* (C) Copyright IBM Corp. 2001, 2003
* Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001-2002 International Business Machines, Corp.
* Copyright (c) 2001 Intel Corp. * Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001 Nokia, Inc. * Copyright (c) 2001 Nokia, Inc.
* Copyright (c) 2001 La Monte H.P. Yarroll * Copyright (c) 2001 La Monte H.P. Yarroll
...@@ -42,6 +42,8 @@ ...@@ -42,6 +42,8 @@
* randall@sctp.chicago.il.us * randall@sctp.chicago.il.us
* kmorneau@cisco.com * kmorneau@cisco.com
* qxie1@email.mot.com * qxie1@email.mot.com
* Sridhar Samudrala <sri@us.ibm.com>
* Kevin Gao <kevin.gao@intel.com>
* *
* Any bugs reported given to us we will try to fix... any fixes shared will * Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release. * be incorporated into the next SCTP release.
...@@ -507,6 +509,11 @@ typedef struct sctp_cwr_chunk { ...@@ -507,6 +509,11 @@ typedef struct sctp_cwr_chunk {
* The ASCONF Parameter Response is used in the ASCONF-ACK to * The ASCONF Parameter Response is used in the ASCONF-ACK to
* report status of ASCONF processing. * report status of ASCONF processing.
*/ */
typedef struct sctp_addip_param {
sctp_paramhdr_t param_hdr;
__u32 crr_id;
}sctp_addip_param_t __attribute__((packed));
typedef struct sctp_addiphdr { typedef struct sctp_addiphdr {
__u32 serial; __u32 serial;
__u8 params[0]; __u8 params[0];
......
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* (C) Copyright IBM Corp. 2001, 2003
* Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001-2003 International Business Machines, Corp.
* Copyright (c) 2001-2003 Intel Corp. * Copyright (c) 2001-2003 Intel Corp.
* *
* This file is part of the SCTP kernel reference Implementation * This file is part of the SCTP kernel reference Implementation
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
* Sridhar Samudrala <sri@us.ibm.com> * Sridhar Samudrala <sri@us.ibm.com>
* Ardelle Fan <ardelle.fan@intel.com> * Ardelle Fan <ardelle.fan@intel.com>
* Ryan Layer <rmlayer@us.ibm.com> * Ryan Layer <rmlayer@us.ibm.com>
* Kevin Gao <kevin.gao@intel.com>
* *
* Any bugs reported given to us we will try to fix... any fixes shared will * Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release. * be incorporated into the next SCTP release.
...@@ -145,6 +146,7 @@ int sctp_primitive_SHUTDOWN(struct sctp_association *, void *arg); ...@@ -145,6 +146,7 @@ int sctp_primitive_SHUTDOWN(struct sctp_association *, void *arg);
int sctp_primitive_ABORT(struct sctp_association *, void *arg); int sctp_primitive_ABORT(struct sctp_association *, void *arg);
int sctp_primitive_SEND(struct sctp_association *, void *arg); int sctp_primitive_SEND(struct sctp_association *, void *arg);
int sctp_primitive_REQUESTHEARTBEAT(struct sctp_association *, void *arg); int sctp_primitive_REQUESTHEARTBEAT(struct sctp_association *, void *arg);
int sctp_primitive_ASCONF(struct sctp_association *, void *arg);
/* /*
* sctp/crc32c.c * sctp/crc32c.c
...@@ -404,6 +406,12 @@ static inline struct list_head *sctp_list_dequeue(struct list_head *list) ...@@ -404,6 +406,12 @@ static inline struct list_head *sctp_list_dequeue(struct list_head *list)
return result; return result;
} }
/* Tests if the list has one and only one entry. */
static inline int sctp_list_single_entry(struct list_head *head)
{
return ((head->next != head) && (head->next == head->prev));
}
/* Calculate the size (in bytes) occupied by the data of an iovec. */ /* Calculate the size (in bytes) occupied by the data of an iovec. */
static inline size_t get_user_iov_size(struct iovec *iov, int iovlen) static inline size_t get_user_iov_size(struct iovec *iov, int iovlen)
{ {
...@@ -525,6 +533,19 @@ static inline int ipver2af(__u8 ipver) ...@@ -525,6 +533,19 @@ static inline int ipver2af(__u8 ipver)
}; };
} }
/* Convert from an address parameter type to an address family. */
static inline int param_type2af(__u16 type)
{
switch (type) {
case SCTP_PARAM_IPV4_ADDRESS:
return AF_INET;
case SCTP_PARAM_IPV6_ADDRESS:
return AF_INET6;
default:
return 0;
};
}
/* Perform some sanity checks. */ /* Perform some sanity checks. */
static inline int sctp_sanity_check(void) static inline int sctp_sanity_check(void)
{ {
......
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* (C) Copyright IBM Corp. 2001, 2003
* Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 Intel Corp. * Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001-2002 International Business Machines Corp.
* *
* This file is part of the SCTP kernel reference Implementation * This file is part of the SCTP kernel reference Implementation
* *
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
* Sridhar Samudrala <sri@us.ibm.com> * Sridhar Samudrala <sri@us.ibm.com>
* Daisy Chang <daisyc@us.ibm.com> * Daisy Chang <daisyc@us.ibm.com>
* Ardelle Fan <ardelle.fan@intel.com> * Ardelle Fan <ardelle.fan@intel.com>
* Kevin Gao <kevin.gao@intel.com>
* *
* Any bugs reported given to us we will try to fix... any fixes shared will * Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release. * be incorporated into the next SCTP release.
...@@ -268,6 +269,9 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, ...@@ -268,6 +269,9 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
struct sctp_chunk *asconf, struct sctp_chunk *asconf,
int vparam_len); int vparam_len);
struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc,
union sctp_addr *addr);
void sctp_chunk_assign_tsn(struct sctp_chunk *); void sctp_chunk_assign_tsn(struct sctp_chunk *);
void sctp_chunk_assign_ssn(struct sctp_chunk *); void sctp_chunk_assign_ssn(struct sctp_chunk *);
...@@ -330,12 +334,6 @@ void sctp_send_stale_cookie_err(const struct sctp_endpoint *ep, ...@@ -330,12 +334,6 @@ void sctp_send_stale_cookie_err(const struct sctp_endpoint *ep,
__u32 sctp_generate_tag(const struct sctp_endpoint *); __u32 sctp_generate_tag(const struct sctp_endpoint *);
__u32 sctp_generate_tsn(const struct sctp_endpoint *); __u32 sctp_generate_tsn(const struct sctp_endpoint *);
/* 4th level prototypes */
void sctp_param2sockaddr(union sctp_addr *addr, union sctp_addr_param *,
__u16 port, int iif);
int sctp_addr2sockaddr(const union sctp_params, union sctp_addr *);
int sockaddr2sctp_addr(const union sctp_addr *, union sctp_addr_param *);
/* Extern declarations for major data structures. */ /* Extern declarations for major data structures. */
const sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t, sctp_state_t); const sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t, sctp_state_t);
extern const sctp_sm_table_entry_t extern const sctp_sm_table_entry_t
......
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* (C) Copyright IBM Corp. 2001, 2003
* Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 Intel Corp. * Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001-2003 International Business Machines Corp.
* *
* This file is part of the SCTP kernel reference Implementation * This file is part of the SCTP kernel reference Implementation
* *
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
* Ardelle Fan <ardelle.fan@intel.com> * Ardelle Fan <ardelle.fan@intel.com>
* Ryan Layer <rmlayer@us.ibm.com> * Ryan Layer <rmlayer@us.ibm.com>
* Anup Pemmaiah <pemmaiah@cc.usu.edu> * Anup Pemmaiah <pemmaiah@cc.usu.edu>
* Kevin Gao <kevin.gao@intel.com>
* *
* Any bugs reported given to us we will try to fix... any fixes shared will * Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release. * be incorporated into the next SCTP release.
...@@ -217,86 +218,6 @@ extern struct sctp_globals { ...@@ -217,86 +218,6 @@ extern struct sctp_globals {
#define sctp_local_addr_list (sctp_globals.local_addr_list) #define sctp_local_addr_list (sctp_globals.local_addr_list)
#define sctp_local_addr_lock (sctp_globals.local_addr_lock) #define sctp_local_addr_lock (sctp_globals.local_addr_lock)
/*
* Pointers to address related SCTP functions.
* (i.e. things that depend on the address family.)
*/
struct sctp_af {
int (*sctp_xmit) (struct sk_buff *skb,
struct sctp_transport *,
int ipfragok);
int (*setsockopt) (struct sock *sk,
int level,
int optname,
char *optval,
int optlen);
int (*getsockopt) (struct sock *sk,
int level,
int optname,
char *optval,
int *optlen);
struct dst_entry *(*get_dst) (struct sctp_association *asoc,
union sctp_addr *daddr,
union sctp_addr *saddr);
void (*get_saddr) (struct sctp_association *asoc,
struct dst_entry *dst,
union sctp_addr *daddr,
union sctp_addr *saddr);
void (*copy_addrlist) (struct list_head *,
struct net_device *);
void (*dst_saddr) (union sctp_addr *saddr,
struct dst_entry *dst,
unsigned short port);
int (*cmp_addr) (const union sctp_addr *addr1,
const union sctp_addr *addr2);
void (*addr_copy) (union sctp_addr *dst,
union sctp_addr *src);
void (*from_skb) (union sctp_addr *,
struct sk_buff *skb,
int saddr);
void (*from_sk) (union sctp_addr *,
struct sock *sk);
void (*to_sk_saddr) (union sctp_addr *,
struct sock *sk);
void (*to_sk_daddr) (union sctp_addr *,
struct sock *sk);
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) (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,
union sctp_addr *addr);
__u16 net_header_len;
int sockaddr_len;
sa_family_t sa_family;
struct list_head list;
};
struct sctp_af *sctp_get_af_specific(sa_family_t);
int sctp_register_af(struct sctp_af *);
/* Protocol family functions. */
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, struct sctp_opt *);
int (*cmp_addr) (const union sctp_addr *,
const union sctp_addr *,
struct sctp_opt *);
int (*bind_verify) (struct sctp_opt *, union sctp_addr *);
int (*send_verify) (struct sctp_opt *, union sctp_addr *);
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;
};
/* SCTP Socket type: UDP or TCP style. */ /* SCTP Socket type: UDP or TCP style. */
typedef enum { typedef enum {
SCTP_SOCKET_UDP = 0, SCTP_SOCKET_UDP = 0,
...@@ -488,6 +409,92 @@ static inline __u16 sctp_ssn_next(struct sctp_stream *stream, __u16 id) ...@@ -488,6 +409,92 @@ static inline __u16 sctp_ssn_next(struct sctp_stream *stream, __u16 id)
return stream->ssn[id]++; return stream->ssn[id]++;
} }
/*
* Pointers to address related SCTP functions.
* (i.e. things that depend on the address family.)
*/
struct sctp_af {
int (*sctp_xmit) (struct sk_buff *skb,
struct sctp_transport *,
int ipfragok);
int (*setsockopt) (struct sock *sk,
int level,
int optname,
char *optval,
int optlen);
int (*getsockopt) (struct sock *sk,
int level,
int optname,
char *optval,
int *optlen);
struct dst_entry *(*get_dst) (struct sctp_association *asoc,
union sctp_addr *daddr,
union sctp_addr *saddr);
void (*get_saddr) (struct sctp_association *asoc,
struct dst_entry *dst,
union sctp_addr *daddr,
union sctp_addr *saddr);
void (*copy_addrlist) (struct list_head *,
struct net_device *);
void (*dst_saddr) (union sctp_addr *saddr,
struct dst_entry *dst,
unsigned short port);
int (*cmp_addr) (const union sctp_addr *addr1,
const union sctp_addr *addr2);
void (*addr_copy) (union sctp_addr *dst,
union sctp_addr *src);
void (*from_skb) (union sctp_addr *,
struct sk_buff *skb,
int saddr);
void (*from_sk) (union sctp_addr *,
struct sock *sk);
void (*to_sk_saddr) (union sctp_addr *,
struct sock *sk);
void (*to_sk_daddr) (union sctp_addr *,
struct sock *sk);
void (*from_addr_param) (union sctp_addr *,
union sctp_addr_param *,
__u16 port, int iif);
int (*to_addr_param) (const union sctp_addr *,
union sctp_addr_param *);
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) (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,
union sctp_addr *addr);
__u16 net_header_len;
int sockaddr_len;
sa_family_t sa_family;
struct list_head list;
};
struct sctp_af *sctp_get_af_specific(sa_family_t);
int sctp_register_af(struct sctp_af *);
/* Protocol family functions. */
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, struct sctp_opt *);
int (*cmp_addr) (const union sctp_addr *,
const union sctp_addr *,
struct sctp_opt *);
int (*bind_verify) (struct sctp_opt *, union sctp_addr *);
int (*send_verify) (struct sctp_opt *, union sctp_addr *);
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;
};
/* Structure to track chunk fragments that have been acked, but peer /* Structure to track chunk fragments that have been acked, but peer
* fragments of the same message have not. * fragments of the same message have not.
*/ */
...@@ -1688,6 +1695,8 @@ struct sctp_transport *sctp_assoc_choose_shutdown_transport( ...@@ -1688,6 +1695,8 @@ struct sctp_transport *sctp_assoc_choose_shutdown_transport(
void sctp_assoc_update_retran_path(struct sctp_association *); void sctp_assoc_update_retran_path(struct sctp_association *);
struct sctp_transport *sctp_assoc_lookup_paddr(const struct sctp_association *, struct sctp_transport *sctp_assoc_lookup_paddr(const struct sctp_association *,
const union sctp_addr *); const union sctp_addr *);
int sctp_assoc_lookup_laddr(struct sctp_association *asoc,
const union sctp_addr *laddr);
struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *, struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *,
const union sctp_addr *address, const union sctp_addr *address,
const int gfp); const int gfp);
......
...@@ -551,13 +551,15 @@ struct sctp_status { ...@@ -551,13 +551,15 @@ struct sctp_status {
}; };
/* /*
* 8.3, 8.5 get all peer/local addresses on a socket * 8.3, 8.5 get all peer/local addresses in an association.
* This parameter struct is for getsockopt * This parameter struct is used by SCTP_GET_PEER_ADDRS and
* SCTP_GET_LOCAL_ADDRS socket options used internally to implement
* sctp_getpaddrs() and sctp_getladdrs() API.
*/ */
struct sctp_getaddrs { struct sctp_getaddrs {
sctp_assoc_t assoc_id; sctp_assoc_t assoc_id;
int addr_num; int addr_num;
struct sockaddr_storage *addrs; struct sockaddr *addrs;
}; };
/* These are bit fields for msghdr->msg_flags. See section 5.1. */ /* These are bit fields for msghdr->msg_flags. See section 5.1. */
......
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* (C) Copyright IBM Corp. 2001, 2003
* Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001-2003 International Business Machines Corp.
* Copyright (c) 2001 Intel Corp. * Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001 La Monte H.P. Yarroll * Copyright (c) 2001 La Monte H.P. Yarroll
* *
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
* Sridhar Samudrala <sri@us.ibm.com> * Sridhar Samudrala <sri@us.ibm.com>
* Daisy Chang <daisyc@us.ibm.com> * Daisy Chang <daisyc@us.ibm.com>
* Ryan Layer <rmlayer@us.ibm.com> * Ryan Layer <rmlayer@us.ibm.com>
* Kevin Gao <kevin.gao@intel.com>
* *
* Any bugs reported given to us we will try to fix... any fixes shared will * Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release. * be incorporated into the next SCTP release.
...@@ -1155,3 +1156,23 @@ int sctp_assoc_set_bind_addr_from_cookie(struct sctp_association *asoc, ...@@ -1155,3 +1156,23 @@ int sctp_assoc_set_bind_addr_from_cookie(struct sctp_association *asoc,
return sctp_raw_to_bind_addrs(&asoc->base.bind_addr, raw, var_size3, return sctp_raw_to_bind_addrs(&asoc->base.bind_addr, raw, var_size3,
asoc->ep->base.bind_addr.port, gfp); asoc->ep->base.bind_addr.port, gfp);
} }
/* Lookup laddr in the bind address list of an association. */
int sctp_assoc_lookup_laddr(struct sctp_association *asoc,
const union sctp_addr *laddr)
{
int found;
sctp_read_lock(&asoc->base.addr_lock);
if ((asoc->base.bind_addr.port == ntohs(laddr->v4.sin_port)) &&
sctp_bind_addr_match(&asoc->base.bind_addr, laddr,
sctp_sk(asoc->base.sk))) {
found = 1;
goto out;
}
found = 0;
out:
sctp_read_unlock(&asoc->base.addr_lock);
return found;
}
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* (C) Copyright IBM Corp. 2001, 2003
* Copyright (c) Cisco 1999,2000 * Copyright (c) Cisco 1999,2000
* Copyright (c) Motorola 1999,2000,2001 * Copyright (c) Motorola 1999,2000,2001
* Copyright (c) International Business Machines Corp., 2001,2002
* Copyright (c) La Monte H.P. Yarroll 2001 * Copyright (c) La Monte H.P. Yarroll 2001
* *
* This file is part of the SCTP kernel reference implementation. * This file is part of the SCTP kernel reference implementation.
...@@ -223,6 +223,8 @@ union sctp_params sctp_bind_addrs_to_raw(const struct sctp_bind_addr *bp, ...@@ -223,6 +223,8 @@ union sctp_params sctp_bind_addrs_to_raw(const struct sctp_bind_addr *bp,
int len; int len;
struct sctp_sockaddr_entry *addr; struct sctp_sockaddr_entry *addr;
struct list_head *pos; struct list_head *pos;
struct sctp_af *af;
addrparms_len = 0; addrparms_len = 0;
len = 0; len = 0;
...@@ -247,7 +249,8 @@ union sctp_params sctp_bind_addrs_to_raw(const struct sctp_bind_addr *bp, ...@@ -247,7 +249,8 @@ union sctp_params sctp_bind_addrs_to_raw(const struct sctp_bind_addr *bp,
list_for_each(pos, &bp->address_list) { list_for_each(pos, &bp->address_list) {
addr = list_entry(pos, struct sctp_sockaddr_entry, list); addr = list_entry(pos, struct sctp_sockaddr_entry, list);
len = sockaddr2sctp_addr(&addr->a, &rawaddr); af = sctp_get_af_specific(addr->a.v4.sin_family);
len = af->to_addr_param(&addr->a, &rawaddr);
memcpy(addrparms.v, &rawaddr, len); memcpy(addrparms.v, &rawaddr, len);
addrparms.v += len; addrparms.v += len;
addrparms_len += len; addrparms_len += len;
...@@ -270,34 +273,31 @@ int sctp_raw_to_bind_addrs(struct sctp_bind_addr *bp, __u8 *raw_addr_list, ...@@ -270,34 +273,31 @@ int sctp_raw_to_bind_addrs(struct sctp_bind_addr *bp, __u8 *raw_addr_list,
union sctp_addr addr; union sctp_addr addr;
int retval = 0; int retval = 0;
int len; int len;
struct sctp_af *af;
/* Convert the raw address to standard address format */ /* Convert the raw address to standard address format */
while (addrs_len) { while (addrs_len) {
param = (struct sctp_paramhdr *)raw_addr_list; param = (struct sctp_paramhdr *)raw_addr_list;
rawaddr = (union sctp_addr_param *)raw_addr_list; rawaddr = (union sctp_addr_param *)raw_addr_list;
switch (param->type) { af = sctp_get_af_specific(param_type2af(param->type));
case SCTP_PARAM_IPV4_ADDRESS: if (unlikely(!af)) {
case SCTP_PARAM_IPV6_ADDRESS:
sctp_param2sockaddr(&addr, rawaddr, port, 0);
retval = sctp_add_bind_addr(bp, &addr, gfp);
if (retval) {
/* Can't finish building the list, clean up. */
sctp_bind_addr_clean(bp);
break;;
}
len = ntohs(param->length);
addrs_len -= len;
raw_addr_list += len;
break;
default:
/* Corrupted raw addr list! */
retval = -EINVAL; retval = -EINVAL;
sctp_bind_addr_clean(bp); sctp_bind_addr_clean(bp);
break; break;
} }
if (retval)
break; af->from_addr_param(&addr, rawaddr, port, 0);
retval = sctp_add_bind_addr(bp, &addr, gfp);
if (retval) {
/* Can't finish building the list, clean up. */
sctp_bind_addr_clean(bp);
break;;
}
len = ntohs(param->length);
addrs_len -= len;
raw_addr_list += len;
} }
return retval; return retval;
......
...@@ -768,6 +768,7 @@ static struct sctp_association *__sctp_rcv_init_lookup(struct sk_buff *skb, ...@@ -768,6 +768,7 @@ static struct sctp_association *__sctp_rcv_init_lookup(struct sk_buff *skb,
union sctp_params params; union sctp_params params;
sctp_init_chunk_t *init; sctp_init_chunk_t *init;
struct sctp_transport *transport; struct sctp_transport *transport;
struct sctp_af *af;
ch = (sctp_chunkhdr_t *) skb->data; ch = (sctp_chunkhdr_t *) skb->data;
...@@ -802,11 +803,12 @@ static struct sctp_association *__sctp_rcv_init_lookup(struct sk_buff *skb, ...@@ -802,11 +803,12 @@ static struct sctp_association *__sctp_rcv_init_lookup(struct sk_buff *skb,
sctp_walk_params(params, init, init_hdr.params) { sctp_walk_params(params, init, init_hdr.params) {
/* Note: Ignoring hostname addresses. */ /* Note: Ignoring hostname addresses. */
if ((SCTP_PARAM_IPV4_ADDRESS != params.p->type) && af = sctp_get_af_specific(param_type2af(params.p->type));
(SCTP_PARAM_IPV6_ADDRESS != params.p->type)) if (!af)
continue; continue;
sctp_param2sockaddr(paddr, params.addr, ntohs(sh->source), 0); af->from_addr_param(paddr, params.addr, ntohs(sh->source), 0);
asoc = __sctp_lookup_association(laddr, paddr, &transport); asoc = __sctp_lookup_association(laddr, paddr, &transport);
if (asoc) if (asoc)
return asoc; return asoc;
......
...@@ -399,6 +399,33 @@ static void sctp_v6_to_sk_daddr(union sctp_addr *addr, struct sock *sk) ...@@ -399,6 +399,33 @@ static void sctp_v6_to_sk_daddr(union sctp_addr *addr, struct sock *sk)
} }
} }
/* Initialize a sctp_addr from an address parameter. */
static void sctp_v6_from_addr_param(union sctp_addr *addr,
union sctp_addr_param *param,
__u16 port, int iif)
{
addr->v6.sin6_family = AF_INET6;
addr->v6.sin6_port = port;
addr->v6.sin6_flowinfo = 0; /* BUG */
ipv6_addr_copy(&addr->v6.sin6_addr, &param->v6.addr);
addr->v6.sin6_scope_id = iif;
}
/* Initialize an address parameter from a sctp_addr and return the length
* of the address parameter.
*/
static int sctp_v6_to_addr_param(const union sctp_addr *addr,
union sctp_addr_param *param)
{
int length = sizeof(sctp_ipv6addr_param_t);
param->v6.param_hdr.type = SCTP_PARAM_IPV6_ADDRESS;
param->v6.param_hdr.length = ntohs(length);
ipv6_addr_copy(&param->v6.addr, &addr->v6.sin6_addr);
return length;
}
/* Initialize a sctp_addr from a dst_entry. */ /* Initialize a sctp_addr from a dst_entry. */
static void sctp_v6_dst_saddr(union sctp_addr *addr, struct dst_entry *dst, static void sctp_v6_dst_saddr(union sctp_addr *addr, struct dst_entry *dst,
unsigned short port) unsigned short port)
...@@ -903,6 +930,8 @@ static struct sctp_af sctp_ipv6_specific = { ...@@ -903,6 +930,8 @@ static struct sctp_af sctp_ipv6_specific = {
.from_sk = sctp_v6_from_sk, .from_sk = sctp_v6_from_sk,
.to_sk_saddr = sctp_v6_to_sk_saddr, .to_sk_saddr = sctp_v6_to_sk_saddr,
.to_sk_daddr = sctp_v6_to_sk_daddr, .to_sk_daddr = sctp_v6_to_sk_daddr,
.from_addr_param = sctp_v6_from_addr_param,
.to_addr_param = sctp_v6_to_addr_param,
.dst_saddr = sctp_v6_dst_saddr, .dst_saddr = sctp_v6_dst_saddr,
.cmp_addr = sctp_v6_cmp_addr, .cmp_addr = sctp_v6_cmp_addr,
.scope = sctp_v6_scope, .scope = sctp_v6_scope,
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
* Narasimha Budihal <narasimha@refcode.org> * Narasimha Budihal <narasimha@refcode.org>
* Karl Knutson <karl@athena.chicago.il.us> * Karl Knutson <karl@athena.chicago.il.us>
* Ardelle Fan <ardelle.fan@intel.com> * Ardelle Fan <ardelle.fan@intel.com>
* Kevin Gao <kevin.gao@intel.com>
* *
* Any bugs reported given to us we will try to fix... any fixes shared will * Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release. * be incorporated into the next SCTP release.
...@@ -203,3 +204,16 @@ DECLARE_PRIMITIVE(SEND); ...@@ -203,3 +204,16 @@ DECLARE_PRIMITIVE(SEND);
*/ */
DECLARE_PRIMITIVE(REQUESTHEARTBEAT); DECLARE_PRIMITIVE(REQUESTHEARTBEAT);
/* ADDIP
* 3.1.1 Address Configuration Change Chunk (ASCONF)
*
* This chunk is used to communicate to the remote endpoint one of the
* configuration change requests that MUST be acknowledged. The
* information carried in the ASCONF Chunk uses the form of a
* Type-Length-Value (TLV), as described in "3.2.1 Optional/
* Variable-length Parameter Format" in RFC2960 [5], forall variable
* parameters.
*/
DECLARE_PRIMITIVE(ASCONF);
...@@ -295,6 +295,31 @@ static void sctp_v4_to_sk_daddr(union sctp_addr *addr, struct sock *sk) ...@@ -295,6 +295,31 @@ static void sctp_v4_to_sk_daddr(union sctp_addr *addr, struct sock *sk)
inet_sk(sk)->daddr = addr->v4.sin_addr.s_addr; inet_sk(sk)->daddr = addr->v4.sin_addr.s_addr;
} }
/* Initialize a sctp_addr from an address parameter. */
static void sctp_v4_from_addr_param(union sctp_addr *addr,
union sctp_addr_param *param,
__u16 port, int iif)
{
addr->v4.sin_family = AF_INET;
addr->v4.sin_port = port;
addr->v4.sin_addr.s_addr = param->v4.addr.s_addr;
}
/* Initialize an address parameter from a sctp_addr and return the length
* of the address parameter.
*/
static int sctp_v4_to_addr_param(const union sctp_addr *addr,
union sctp_addr_param *param)
{
int length = sizeof(sctp_ipv4addr_param_t);
param->v4.param_hdr.type = SCTP_PARAM_IPV4_ADDRESS;
param->v4.param_hdr.length = ntohs(length);
param->v4.addr.s_addr = addr->v4.sin_addr.s_addr;
return length;
}
/* Initialize a sctp_addr from a dst_entry. */ /* Initialize a sctp_addr from a dst_entry. */
static void sctp_v4_dst_saddr(union sctp_addr *saddr, struct dst_entry *dst, static void sctp_v4_dst_saddr(union sctp_addr *saddr, struct dst_entry *dst,
unsigned short port) unsigned short port)
...@@ -862,6 +887,8 @@ struct sctp_af sctp_ipv4_specific = { ...@@ -862,6 +887,8 @@ struct sctp_af sctp_ipv4_specific = {
.from_sk = sctp_v4_from_sk, .from_sk = sctp_v4_from_sk,
.to_sk_saddr = sctp_v4_to_sk_saddr, .to_sk_saddr = sctp_v4_to_sk_saddr,
.to_sk_daddr = sctp_v4_to_sk_daddr, .to_sk_daddr = sctp_v4_to_sk_daddr,
.from_addr_param= sctp_v4_from_addr_param,
.to_addr_param = sctp_v4_to_addr_param,
.dst_saddr = sctp_v4_dst_saddr, .dst_saddr = sctp_v4_dst_saddr,
.cmp_addr = sctp_v4_cmp_addr, .cmp_addr = sctp_v4_cmp_addr,
.addr_valid = sctp_v4_addr_valid, .addr_valid = sctp_v4_addr_valid,
......
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* Copyright (C) IBM Corp. 2001, 2003
* Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001-2002 Intel Corp. * Copyright (c) 2001-2002 Intel Corp.
* Copyright (c) 2001-2002 International Business Machines Corp.
* *
* This file is part of the SCTP kernel reference Implementation * This file is part of the SCTP kernel reference Implementation
* *
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
* Sridhar Samudrala <sri@us.ibm.com> * Sridhar Samudrala <sri@us.ibm.com>
* Daisy Chang <daisyc@us.ibm.com> * Daisy Chang <daisyc@us.ibm.com>
* Ardelle Fan <ardelle.fan@intel.com> * Ardelle Fan <ardelle.fan@intel.com>
* Kevin Gao <kevin.gao@intel.com>
* *
* Any bugs reported given to us we will try to fix... any fixes shared will * Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release. * be incorporated into the next SCTP release.
...@@ -1213,6 +1214,7 @@ struct sctp_association *sctp_make_temp_asoc(const struct sctp_endpoint *ep, ...@@ -1213,6 +1214,7 @@ struct sctp_association *sctp_make_temp_asoc(const struct sctp_endpoint *ep,
struct sctp_association *asoc; struct sctp_association *asoc;
struct sk_buff *skb; struct sk_buff *skb;
sctp_scope_t scope; sctp_scope_t scope;
struct sctp_af *af;
/* Create the bare association. */ /* Create the bare association. */
scope = sctp_scope(sctp_source(chunk)); scope = sctp_scope(sctp_source(chunk));
...@@ -1222,29 +1224,10 @@ struct sctp_association *sctp_make_temp_asoc(const struct sctp_endpoint *ep, ...@@ -1222,29 +1224,10 @@ struct sctp_association *sctp_make_temp_asoc(const struct sctp_endpoint *ep,
asoc->temp = 1; asoc->temp = 1;
skb = chunk->skb; skb = chunk->skb;
/* Create an entry for the source address of the packet. */ /* Create an entry for the source address of the packet. */
/* FIXME: Use the af specific helpers. */ af = sctp_get_af_specific(ipver2af(skb->nh.iph->version));
switch (skb->nh.iph->version) { if (unlikely(!af))
case 4:
asoc->c.peer_addr.v4.sin_family = AF_INET;
asoc->c.peer_addr.v4.sin_port = ntohs(chunk->sctp_hdr->source);
asoc->c.peer_addr.v4.sin_addr.s_addr = skb->nh.iph->saddr;
break;
case 6:
asoc->c.peer_addr.v6.sin6_family = AF_INET6;
asoc->c.peer_addr.v6.sin6_port
= ntohs(chunk->sctp_hdr->source);
asoc->c.peer_addr.v6.sin6_flowinfo = 0; /* BUG BUG BUG */
asoc->c.peer_addr.v6.sin6_addr = skb->nh.ipv6h->saddr;
asoc->c.peer_addr.v6.sin6_scope_id =
((struct inet6_skb_parm *)skb->cb)->iif;
break;
default:
/* Yikes! I never heard of this kind of address. */
goto fail; goto fail;
}; af->from_skb(&asoc->c.peer_addr, skb, 1);
nodata: nodata:
return asoc; return asoc;
...@@ -1830,11 +1813,14 @@ int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid, ...@@ -1830,11 +1813,14 @@ int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid,
* stream sequence number shall be set to 0. * stream sequence number shall be set to 0.
*/ */
/* Allocate storage for the negotiated streams. */ /* Allocate storage for the negotiated streams if it is not a temporary * association.
asoc->ssnmap = sctp_ssnmap_new(asoc->peer.i.num_outbound_streams, */
asoc->c.sinit_num_ostreams, gfp); if (!asoc->temp) {
if (!asoc->ssnmap) asoc->ssnmap = sctp_ssnmap_new(asoc->c.sinit_max_instreams,
goto nomem_ssnmap; asoc->c.sinit_num_ostreams, gfp);
if (!asoc->ssnmap)
goto nomem_ssnmap;
}
/* ADDIP Section 4.1 ASCONF Chunk Procedures /* ADDIP Section 4.1 ASCONF Chunk Procedures
* *
...@@ -1882,6 +1868,7 @@ int sctp_process_param(struct sctp_association *asoc, union sctp_params param, ...@@ -1882,6 +1868,7 @@ int sctp_process_param(struct sctp_association *asoc, union sctp_params param,
int retval = 1; int retval = 1;
sctp_scope_t scope; sctp_scope_t scope;
time_t stale; time_t stale;
struct sctp_af *af;
/* We maintain all INIT parameters in network byte order all the /* We maintain all INIT parameters in network byte order all the
* time. This allows us to not worry about whether the parameters * time. This allows us to not worry about whether the parameters
...@@ -1893,7 +1880,8 @@ int sctp_process_param(struct sctp_association *asoc, union sctp_params param, ...@@ -1893,7 +1880,8 @@ int sctp_process_param(struct sctp_association *asoc, union sctp_params param,
break; break;
/* Fall through. */ /* Fall through. */
case SCTP_PARAM_IPV4_ADDRESS: case SCTP_PARAM_IPV4_ADDRESS:
sctp_param2sockaddr(&addr, param.addr, asoc->peer.port, 0); af = sctp_get_af_specific(param_type2af(param.p->type));
af->from_addr_param(&addr, param.addr, asoc->peer.port, 0);
scope = sctp_scope(peer_addr); scope = sctp_scope(peer_addr);
if (sctp_in_scope(&addr, scope)) if (sctp_in_scope(&addr, scope))
if (!sctp_assoc_add_peer(asoc, &addr, gfp)) if (!sctp_assoc_add_peer(asoc, &addr, gfp))
...@@ -2005,90 +1993,6 @@ __u32 sctp_generate_tsn(const struct sctp_endpoint *ep) ...@@ -2005,90 +1993,6 @@ __u32 sctp_generate_tsn(const struct sctp_endpoint *ep)
return retval; return retval;
} }
/********************************************************************
* 4th Level Abstractions
********************************************************************/
/* Convert from an SCTP IP parameter to a union sctp_addr. */
void sctp_param2sockaddr(union sctp_addr *addr, union sctp_addr_param *param,
__u16 port, int iif)
{
switch(param->v4.param_hdr.type) {
case SCTP_PARAM_IPV4_ADDRESS:
addr->v4.sin_family = AF_INET;
addr->v4.sin_port = port;
addr->v4.sin_addr.s_addr = param->v4.addr.s_addr;
break;
case SCTP_PARAM_IPV6_ADDRESS:
addr->v6.sin6_family = AF_INET6;
addr->v6.sin6_port = port;
addr->v6.sin6_flowinfo = 0; /* BUG */
addr->v6.sin6_addr = param->v6.addr;
addr->v6.sin6_scope_id = iif;
break;
default:
SCTP_DEBUG_PRINTK("Illegal address type %d\n",
ntohs(param->v4.param_hdr.type));
break;
};
}
/* Convert an IP address in an SCTP param into a sockaddr_in. */
/* Returns true if a valid conversion was possible. */
int sctp_addr2sockaddr(union sctp_params p, union sctp_addr *sa)
{
switch (p.p->type) {
case SCTP_PARAM_IPV4_ADDRESS:
sa->v4.sin_addr = *((struct in_addr *)&p.v4->addr);
sa->v4.sin_family = AF_INET;
break;
case SCTP_PARAM_IPV6_ADDRESS:
*((struct in6_addr *)&sa->v4.sin_addr)
= p.v6->addr;
sa->v4.sin_family = AF_INET6;
break;
default:
return 0;
};
return 1;
}
/* Convert a sockaddr_in to an IP address in an SCTP param.
* Returns len if a valid conversion was possible.
*/
int sockaddr2sctp_addr(const union sctp_addr *sa, union sctp_addr_param *p)
{
int len = 0;
switch (sa->v4.sin_family) {
case AF_INET:
p->v4.param_hdr.type = SCTP_PARAM_IPV4_ADDRESS;
p->v4.param_hdr.length = ntohs(sizeof(sctp_ipv4addr_param_t));
len = sizeof(sctp_ipv4addr_param_t);
p->v4.addr.s_addr = sa->v4.sin_addr.s_addr;
break;
case AF_INET6:
p->v6.param_hdr.type = SCTP_PARAM_IPV6_ADDRESS;
p->v6.param_hdr.length = ntohs(sizeof(sctp_ipv6addr_param_t));
len = sizeof(sctp_ipv6addr_param_t);
p->v6.addr = *(&sa->v6.sin6_addr);
break;
default:
printk(KERN_WARNING "sockaddr2sctp_addr: Illegal family %d.\n",
sa->v4.sin_family);
return 0;
};
return len;
}
/* /*
* ADDIP 3.1.1 Address Configuration Change Chunk (ASCONF) * ADDIP 3.1.1 Address Configuration Change Chunk (ASCONF)
* 0 1 2 3 * 0 1 2 3
...@@ -2119,8 +2023,9 @@ struct sctp_chunk *sctp_make_asconf(struct sctp_association *asoc, ...@@ -2119,8 +2023,9 @@ struct sctp_chunk *sctp_make_asconf(struct sctp_association *asoc,
int length = sizeof(asconf) + vparam_len; int length = sizeof(asconf) + vparam_len;
union sctp_params addrparam; union sctp_params addrparam;
int addrlen; int addrlen;
struct sctp_af *af = sctp_get_af_specific(addr->v4.sin_family);
addrlen = sockaddr2sctp_addr(addr, (union sctp_addr_param *)&addrparam); addrlen = af->to_addr_param(addr, (union sctp_addr_param *)&addrparam);
if (!addrlen) if (!addrlen)
return NULL; return NULL;
length += addrlen; length += addrlen;
...@@ -2135,7 +2040,51 @@ struct sctp_chunk *sctp_make_asconf(struct sctp_association *asoc, ...@@ -2135,7 +2040,51 @@ struct sctp_chunk *sctp_make_asconf(struct sctp_association *asoc,
retval->subh.addip_hdr = retval->subh.addip_hdr =
sctp_addto_chunk(retval, sizeof(asconf), &asconf); sctp_addto_chunk(retval, sizeof(asconf), &asconf);
retval->param_hdr.v = retval->param_hdr.v =
sctp_addto_chunk(retval, addrlen, &addr); sctp_addto_chunk(retval, addrlen, &addrparam);
return retval;
}
/* ADDIP
* 3.2.4 Set Primary IP Address
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Type =0xC004 | Length = Variable |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | ASCONF-Request Correlation ID |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Address Parameter |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* Create an ASCONF chunk with Set Primary IP address parameter.
*/
struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc,
union sctp_addr *addr)
{
sctp_addip_param_t param;
struct sctp_chunk *retval;
int len = sizeof(param);
union sctp_params addrparam;
int addrlen;
struct sctp_af *af = sctp_get_af_specific(addr->v4.sin_family);
addrlen = af->to_addr_param(addr, (union sctp_addr_param *)&addrparam);
if (!addrlen)
return NULL;
len += addrlen;
/* Create the chunk and make asconf header. */
retval = sctp_make_asconf(asoc, addr, len);
if (!retval)
return NULL;
param.param_hdr.type = SCTP_PARAM_SET_PRIMARY;
param.param_hdr.length = htons(len);
param.crr_id = 0;
sctp_addto_chunk(retval, sizeof(param), &param);
sctp_addto_chunk(retval, addrlen, &addrparam);
return retval; return retval;
} }
......
...@@ -929,6 +929,7 @@ static int sctp_sf_send_restart_abort(union sctp_addr *ssa, ...@@ -929,6 +929,7 @@ static int sctp_sf_send_restart_abort(union sctp_addr *ssa,
struct sctp_errhdr *errhdr; struct sctp_errhdr *errhdr;
struct sctp_endpoint *ep; struct sctp_endpoint *ep;
char buffer[sizeof(struct sctp_errhdr)+sizeof(union sctp_addr_param)]; char buffer[sizeof(struct sctp_errhdr)+sizeof(union sctp_addr_param)];
struct sctp_af *af = sctp_get_af_specific(ssa->v4.sin_family);
/* Build the error on the stack. We are way to malloc crazy /* Build the error on the stack. We are way to malloc crazy
* throughout the code today. * throughout the code today.
...@@ -937,7 +938,7 @@ static int sctp_sf_send_restart_abort(union sctp_addr *ssa, ...@@ -937,7 +938,7 @@ static int sctp_sf_send_restart_abort(union sctp_addr *ssa,
addrparm = (union sctp_addr_param *)errhdr->variable; addrparm = (union sctp_addr_param *)errhdr->variable;
/* Copy into a parm format. */ /* Copy into a parm format. */
len = sockaddr2sctp_addr(ssa, addrparm); len = af->to_addr_param(ssa, addrparm);
len += sizeof(sctp_errhdr_t); len += sizeof(sctp_errhdr_t);
errhdr->cause = SCTP_ERROR_RESTART; errhdr->cause = SCTP_ERROR_RESTART;
......
This diff is collapsed.
...@@ -214,7 +214,7 @@ static int sctp_sysctl_jiffies_ms(ctl_table *table, int __user *name, int nlen, ...@@ -214,7 +214,7 @@ static int sctp_sysctl_jiffies_ms(ctl_table *table, int __user *name, int nlen,
if (olen != sizeof (int)) if (olen != sizeof (int))
return -EINVAL; return -EINVAL;
} }
if (put_user((*(int *)(table->data) / HZ) * 1000, if (put_user((*(int *)(table->data) * 1000) / HZ,
(int *)oldval) || (int *)oldval) ||
(oldlenp && put_user(sizeof (int), oldlenp))) (oldlenp && put_user(sizeof (int), oldlenp)))
return -EFAULT; return -EFAULT;
...@@ -228,7 +228,7 @@ static int sctp_sysctl_jiffies_ms(ctl_table *table, int __user *name, int nlen, ...@@ -228,7 +228,7 @@ static int sctp_sysctl_jiffies_ms(ctl_table *table, int __user *name, int nlen,
if (get_user(new, (int *)newval)) if (get_user(new, (int *)newval))
return -EFAULT; return -EFAULT;
*(int *)(table->data) = (new * HZ) * 1000; *(int *)(table->data) = (new * HZ) / 1000;
} }
return 1; return 1;
} }
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