[SCTP]: Add the handling of "Set Primary IP Address" parameter to INIT
[pandora-kernel.git] / net / sctp / sm_make_chunk.c
index f487629..8138bbd 100644 (file)
@@ -210,6 +210,9 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc,
        chunksize = sizeof(init) + addrs_len + SCTP_SAT_LEN(num_types);
        chunksize += sizeof(ecap_param);
 
+       if (sctp_prsctp_enable)
+               chunksize += sizeof(prsctp_param);
+
        /* ADDIP: Section 4.2.7:
         *  An implementation supporting this extension [ADDIP] MUST list
         *  the ASCONF,the ASCONF-ACK, and the AUTH  chunks in its INIT and
@@ -286,7 +289,7 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc,
 
        sctp_addto_chunk(retval, sizeof(ecap_param), &ecap_param);
 
-       /* Add the supported extensions paramter.  Be nice and add this
+       /* Add the supported extensions parameter.  Be nice and add this
         * fist before addiding the parameters for the extensions themselves
         */
        if (num_ext) {
@@ -369,6 +372,9 @@ struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc,
        if (asoc->peer.ecn_capable)
                chunksize += sizeof(ecap_param);
 
+       if (sctp_prsctp_enable)
+               chunksize += sizeof(prsctp_param);
+
        if (sctp_addip_enable) {
                extensions[num_ext] = SCTP_CID_ASCONF;
                extensions[num_ext+1] = SCTP_CID_ASCONF_ACK;
@@ -1963,6 +1969,11 @@ static sctp_ierror_t sctp_verify_param(const struct sctp_association *asoc,
        case SCTP_PARAM_SUPPORTED_EXT:
                break;
 
+       case SCTP_PARAM_SET_PRIMARY:
+               if (sctp_addip_enable)
+                       break;
+               goto fallthrough;
+
        case SCTP_PARAM_HOST_NAME_ADDRESS:
                /* Tell the peer, we won't support this param.  */
                sctp_process_hn_param(asoc, param, chunk, err_chunk);
@@ -2280,6 +2291,8 @@ static int sctp_process_param(struct sctp_association *asoc,
        sctp_scope_t scope;
        time_t stale;
        struct sctp_af *af;
+       union sctp_addr_param *addr_param;
+       struct sctp_transport *t;
 
        /* We maintain all INIT parameters in network byte order all the
         * time.  This allows us to not worry about whether the parameters
@@ -2370,6 +2383,26 @@ static int sctp_process_param(struct sctp_association *asoc,
                asoc->peer.adaptation_ind = param.aind->adaptation_ind;
                break;
 
+       case SCTP_PARAM_SET_PRIMARY:
+               addr_param = param.v + sizeof(sctp_addip_param_t);
+
+               af = sctp_get_af_specific(param_type2af(param.p->type));
+               af->from_addr_param(&addr, addr_param,
+                                   htons(asoc->peer.port), 0);
+
+               /* if the address is invalid, we can't process it.
+                * XXX: see spec for what to do.
+                */
+               if (!af->addr_valid(&addr, NULL, NULL))
+                       break;
+
+               t = sctp_assoc_lookup_paddr(asoc, &addr);
+               if (!t)
+                       break;
+
+               sctp_assoc_set_primary(asoc, t);
+               break;
+
        case SCTP_PARAM_SUPPORTED_EXT:
                sctp_process_ext_param(asoc, param);
                break;
@@ -2721,7 +2754,6 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
        struct sctp_transport *peer;
        struct sctp_af *af;
        union sctp_addr addr;
-       struct list_head *pos;
        union sctp_addr_param *addr_param;
 
        addr_param = (union sctp_addr_param *)
@@ -2732,8 +2764,24 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
                return SCTP_ERROR_INV_PARAM;
 
        af->from_addr_param(&addr, addr_param, htons(asoc->peer.port), 0);
+
+       /* ADDIP 4.2.1  This parameter MUST NOT contain a broadcast
+        * or multicast address.
+        * (note: wildcard is permitted and requires special handling so
+        *  make sure we check for that)
+        */
+       if (!af->is_any(&addr) && !af->addr_valid(&addr, NULL, asconf->skb))
+               return SCTP_ERROR_INV_PARAM;
+
        switch (asconf_param->param_hdr.type) {
        case SCTP_PARAM_ADD_IP:
+               /* Section 4.2.1:
+                * If the address 0.0.0.0 or ::0 is provided, the source
+                * address of the packet MUST be added.
+                */
+               if (af->is_any(&addr))
+                       memcpy(&addr, &asconf->source, sizeof(addr));
+
                /* ADDIP 4.3 D9) If an endpoint receives an ADD IP address
                 * request and does not have the local resources to add this
                 * new address to the association, it MUST return an Error
@@ -2755,8 +2803,7 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
                 * MUST send an Error Cause TLV with the error cause set to the
                 * new error code 'Request to Delete Last Remaining IP Address'.
                 */
-               pos = asoc->peer.transport_addr_list.next;
-               if (pos->next == &asoc->peer.transport_addr_list)
+               if (asoc->peer.transport_count == 1)
                        return SCTP_ERROR_DEL_LAST_IP;
 
                /* ADDIP 4.3 D8) If a request is received to delete an IP
@@ -2769,9 +2816,27 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
                if (sctp_cmp_addr_exact(sctp_source(asconf), &addr))
                        return SCTP_ERROR_DEL_SRC_IP;
 
-               sctp_assoc_del_peer(asoc, &addr);
+               /* Section 4.2.2
+                * If the address 0.0.0.0 or ::0 is provided, all
+                * addresses of the peer except the source address of the
+                * packet MUST be deleted.
+                */
+               if (af->is_any(&addr)) {
+                       sctp_assoc_set_primary(asoc, asconf->transport);
+                       sctp_assoc_del_nonprimary_peers(asoc,
+                                                       asconf->transport);
+               } else
+                       sctp_assoc_del_peer(asoc, &addr);
                break;
        case SCTP_PARAM_SET_PRIMARY:
+               /* ADDIP Section 4.2.4
+                * If the address 0.0.0.0 or ::0 is provided, the receiver
+                * MAY mark the source address of the packet as its
+                * primary.
+                */
+               if (af->is_any(&addr))
+                       memcpy(&addr.v4, sctp_source(asconf), sizeof(addr));
+
                peer = sctp_assoc_lookup_paddr(asoc, &addr);
                if (!peer)
                        return SCTP_ERROR_INV_PARAM;
@@ -2859,7 +2924,7 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
        chunk_len -= length;
 
        /* Skip the address parameter and store a pointer to the first
-        * asconf paramter.
+        * asconf parameter.
         */
        length = ntohs(addr_param->v4.param_hdr.length);
        asconf_param = (sctp_addip_param_t *)((void *)addr_param + length);
@@ -2868,7 +2933,7 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
        /* create an ASCONF_ACK chunk.
         * Based on the definitions of parameters, we know that the size of
         * ASCONF_ACK parameters are less than or equal to the twice of ASCONF
-        * paramters.
+        * parameters.
         */
        asconf_ack = sctp_make_asconf_ack(asoc, serial, chunk_len * 2);
        if (!asconf_ack)
@@ -3062,7 +3127,7 @@ int sctp_process_asconf_ack(struct sctp_association *asoc,
        asconf_len -= length;
 
        /* Skip the address parameter in the last asconf sent and store a
-        * pointer to the first asconf paramter.
+        * pointer to the first asconf parameter.
         */
        length = ntohs(addr_param->v4.param_hdr.length);
        asconf_param = (sctp_addip_param_t *)((void *)addr_param + length);