#include <net/sctp/sctp.h>
#include <net/sctp/sm.h>
#include <net/sctp/checksum.h>
+#include <net/net_namespace.h>
/* Forward declarations for internal helpers. */
static int sctp_rcv_ootb(struct sk_buff *);
/* Calculate the SCTP checksum of an SCTP packet. */
static inline int sctp_rcv_checksum(struct sk_buff *skb)
{
- struct sk_buff *list = skb_shinfo(skb)->frag_list;
struct sctphdr *sh = sctp_hdr(skb);
- __u32 cmp = ntohl(sh->checksum);
- __u32 val = sctp_start_cksum((__u8 *)sh, skb_headlen(skb));
+ __le32 cmp = sh->checksum;
+ struct sk_buff *list;
+ __le32 val;
+ __u32 tmp = sctp_start_cksum((__u8 *)sh, skb_headlen(skb));
- for (; list; list = list->next)
- val = sctp_update_cksum((__u8 *)list->data, skb_headlen(list),
- val);
+ skb_walk_frags(skb, list)
+ tmp = sctp_update_cksum((__u8 *)list->data, skb_headlen(list),
+ tmp);
- val = sctp_end_cksum(val);
+ val = sctp_end_cksum(tmp);
if (val != cmp) {
/* CRC failure, dump it. */
__skb_pull(skb, skb_transport_offset(skb));
if (skb->len < sizeof(struct sctphdr))
goto discard_it;
- if (!skb_csum_unnecessary(skb) && sctp_rcv_checksum(skb) < 0)
+ if (!sctp_checksum_disable && !skb_csum_unnecessary(skb) &&
+ sctp_rcv_checksum(skb) < 0)
goto discard_it;
skb_pull(skb, sizeof(struct sctphdr));
*/
sctp_bh_lock_sock(sk);
+ if (sk != rcvr->sk) {
+ /* Our cached sk is different from the rcvr->sk. This is
+ * because migrate()/accept() may have moved the association
+ * to a new socket and released all the sockets. So now we
+ * are holding a lock on the old socket while the user may
+ * be doing something with the new socket. Switch our veiw
+ * of the current sk.
+ */
+ sctp_bh_unlock_sock(sk);
+ sk = rcvr->sk;
+ sctp_bh_lock_sock(sk);
+ }
+
if (sock_owned_by_user(sk)) {
SCTP_INC_STATS_BH(SCTP_MIB_IN_PKT_BACKLOG);
sctp_add_backlog(sk, skb);
void sctp_icmp_frag_needed(struct sock *sk, struct sctp_association *asoc,
struct sctp_transport *t, __u32 pmtu)
{
- if (!t || (t->pathmtu == pmtu))
+ if (!t || (t->pathmtu <= pmtu))
return;
if (sock_owned_by_user(sk)) {
* servers this needs to be solved differently.
*/
if (sock_owned_by_user(sk))
- NET_INC_STATS_BH(LINUX_MIB_LOCKDROPPEDICMPS);
+ NET_INC_STATS_BH(&init_net, LINUX_MIB_LOCKDROPPEDICMPS);
*app = asoc;
*tpp = transport;
int err;
if (skb->len < ihlen + 8) {
- ICMP_INC_STATS_BH(ICMP_MIB_INERRORS);
+ ICMP_INC_STATS_BH(&init_net, ICMP_MIB_INERRORS);
return;
}
skb->network_header = saveip;
skb->transport_header = savesctp;
if (!sk) {
- ICMP_INC_STATS_BH(ICMP_MIB_INERRORS);
+ ICMP_INC_STATS_BH(&init_net, ICMP_MIB_INERRORS);
return;
}
/* Warning: The sock lock is held. Remember to call