javawiresharkrestcommss7

SendParameters weird behavior in jSS7


So i have a forked version of restcomm/jss7 where i implemented the SendParameters SS7 command that is defined for MAP v1, since the version i forked didn't have it implemented at the time (don't think it's implemented still). This is not the first command that i implemented in JSS7. The issue is that I'm pretty sure my encoding/decoding follows the 09.02 spec, but when i construct an invoke SendParameters message given TMSI instead of IMSI in the subscriberId part it is shown in wireshark as msisdn and not parsed correctly. Is this an issue with wireshark or is my implementation at fault? My SendParametersRequestImpl and SubscriberIdImpl for reference:

public class SendParameterRequestImp extends MobilityMessageImpl implements SendParametersRequest {

    private static final int _TAG_SUBSCRIBER_ID = 0;
    private static final int _TAG_REQUESTED_PARAMETER = 1;

    private SubscriberId subscriberId;
    private static final String _PrimitiveName = "SendParameterRequestImp";

    private ArrayList<RequestParameter> requestParameterList;

    public SendParameterRequestImp(SubscriberId subscriberId, ArrayList<RequestParameter> requestParameters) {
        this.subscriberId = subscriberId;
        this.requestParameterList = requestParameters;
    }

    public SendParameterRequestImp() {
    }

    @Override
    public ArrayList<RequestParameter> getRequestParameterList() {
        return requestParameterList;
    }

    @Override
    public SubscriberId getSubscriberId() {
        return subscriberId;
    }

    public void setSubscriberId(SubscriberId subscriberId) {
        this.subscriberId = subscriberId;
    }


    @Override
    public MAPMessageType getMessageType() {
        return MAPMessageType.sendParameter_Request;
    }

    @Override
    public int getOperationCode() {
        return MAPOperationCode.sendParameters;
    }

    @Override
    public int getTag() throws MAPException {
        return Tag.SEQUENCE;
    }

    @Override
    public int getTagClass() {
        return Tag.CLASS_UNIVERSAL;
    }

    @Override
    public boolean getIsPrimitive() {
        return false;
    }

    @Override
    public void decodeAll(AsnInputStream ansIS) throws MAPParsingComponentException {
        try {
            int length = ansIS.readLength();
            this._decode(ansIS, length);
        } catch (IOException e) {
            throw new MAPParsingComponentException("IOException when decoding " + _PrimitiveName + ": " + e.getMessage(), e,
                    MAPParsingComponentExceptionReason.MistypedParameter);
        } catch (AsnException e) {
            throw new MAPParsingComponentException("AsnException when decoding " + _PrimitiveName + ": " + e.getMessage(), e,
                    MAPParsingComponentExceptionReason.MistypedParameter);
        }
    }

    private void _decode(AsnInputStream ansIS, int length) throws IOException, AsnException, MAPParsingComponentException {
        AsnInputStream ais = ansIS.readSequenceStreamData(length);
        while (true) {
            if (ais.available() == 0)
                break;
            int tag = ais.readTag();
            switch (ais.getTagClass()) {
                case Tag.CLASS_CONTEXT_SPECIFIC:
                    this.subscriberId = new SubscriberIdImpl();
                    ((SubscriberIdImpl) this.subscriberId).decodeAll(ais);
                    break;
                case Tag.CLASS_UNIVERSAL:
                    switch (tag) {
                        case Tag.SEQUENCE:
                            this.requestParameterList = new ArrayList<RequestParameter>();
                            AsnInputStream ais3 = ais.readSequenceStream();

                            while (true) {
                                if (ais3.available() == 0)
                                    break;

                                int tag2 = ais3.readTag();

                                if (tag2 != Tag.ENUMERATED)
                                    throw new MAPParsingComponentException("Error while decoding " + _PrimitiveName
                                            + ": bad tag or tagClass or is not primitive when decoding plmnClientList",
                                            MAPParsingComponentExceptionReason.MistypedParameter);

                                int code = (int) ais3.readInteger();

                                RequestParameter elem = RequestParameter.getInstance(code);

                                this.requestParameterList.add(elem);
                            }
                            break;
                        default:
                            ais.advanceElement();
                            break;
                    }
                    break;
                default:
                    ais.advanceElement();
                    break;
            }
        }

    }

    @Override
    public void decodeData(AsnInputStream ansIS, int length) throws MAPParsingComponentException {
        try {
            this._decode(ansIS, length);
        } catch (IOException e) {
            throw new MAPParsingComponentException("IOException when decoding " + _PrimitiveName + ": " + e.getMessage(), e,
                    MAPParsingComponentExceptionReason.MistypedParameter);
        } catch (AsnException e) {
            throw new MAPParsingComponentException("AsnException when decoding " + _PrimitiveName + ": " + e.getMessage(), e,
                    MAPParsingComponentExceptionReason.MistypedParameter);
        }
    }

    @Override
    public void encodeAll(AsnOutputStream asnOs) throws MAPException {
        this.encodeAll(asnOs, this.getTagClass(), this.getTag());
    }

    @Override
    public void encodeAll(AsnOutputStream asnOs, int tagClass, int tag) throws MAPException {
        try {
            asnOs.writeTag(tagClass, this.getIsPrimitive(), tag);
            int pos = asnOs.StartContentDefiniteLength();
            this.encodeData(asnOs);
            asnOs.FinalizeContent(pos);
        } catch (AsnException e) {
            throw new MAPException("AsnException when encoding " + _PrimitiveName + ": " + e.getMessage(), e);

        }
    }

    @Override
    public void encodeData(AsnOutputStream asnOs) throws MAPException {
        if (this.subscriberId == null || this.requestParameterList == null) {
            throw new MAPException("Error while encoding " + _PrimitiveName
                    + " the mandatory parameter subscriberId and requestParameterList is not defined");
        }
        ((SubscriberIdImpl) this.subscriberId).encodeAll(asnOs);
        try {
            asnOs.writeTag(Tag.CLASS_UNIVERSAL, false, Tag.SEQUENCE);
            int pos = asnOs.StartContentDefiniteLength();
            for (RequestParameter requestParameter : this.requestParameterList)
                asnOs.writeInteger(Tag.CLASS_UNIVERSAL, Tag.ENUMERATED, requestParameter.getCode());
            asnOs.FinalizeContent(pos);

        } catch (IOException e) {
            e.printStackTrace();
        } catch (AsnException e) {
            e.printStackTrace();
        }
    }

    @Override
    public String toString() {
        return "SendParameterRequestImp{" +
                "subscriberId=" + subscriberId +
                ", requestParameterList=" + requestParameterList +
                '}';
    }
}
public class SubscriberIdImpl implements SubscriberId, MAPAsnPrimitive {
    private TMSI tmsi;
    private IMSI imsi;

    private static final int _TAG_IMSI = 0;
    private static final int _TAG_TMSI = 1;
    private static final String _PrimitiveName = "SubscriberId";

    public SubscriberIdImpl() {
    }

    public SubscriberIdImpl(TMSI tmsi) {
        this.tmsi = tmsi;
    }

    public SubscriberIdImpl(IMSI imsi) {
        this.imsi = imsi;
    }

    @Override
    public TMSI getTmsi() {
        return tmsi;
    }

    @Override
    public IMSI getImsi() {
        return imsi;
    }

    @Override
    public int getTag() throws MAPException {
        if (this.imsi != null) {
            return _TAG_IMSI;
        } else {
            return _TAG_TMSI;
        }
    }

    @Override
    public int getTagClass() {
        return Tag.CLASS_CONTEXT_SPECIFIC;
    }

    @Override
    public boolean getIsPrimitive() {
        return true;
    }

    @Override
    public void decodeAll(AsnInputStream ansIS) throws MAPParsingComponentException {
        try {
            int length = ansIS.readLength();
            this._decode(ansIS, length);
        } catch (IOException e) {
            throw new MAPParsingComponentException("IOException when decoding " + _PrimitiveName + ": ", e,
                    MAPParsingComponentExceptionReason.MistypedParameter);
        }
    }

    private void _decode(AsnInputStream asnIS, int length) throws MAPParsingComponentException {
        if (asnIS.getTagClass() != Tag.CLASS_CONTEXT_SPECIFIC || !asnIS.isTagPrimitive())
            throw new MAPParsingComponentException("Error while decoding " + _PrimitiveName
                    + ": bad tag class or is not primitive: TagClass=" + asnIS.getTagClass(),
                    MAPParsingComponentExceptionReason.MistypedParameter);

        switch (asnIS.getTag()) {
            case _TAG_IMSI:
                this.imsi = new IMSIImpl();
                ((IMSIImpl) this.imsi).decodeData(asnIS, length);
                break;
            case _TAG_TMSI:
                this.tmsi = new TMSIImpl();
                ((TMSIImpl) this.tmsi).decodeData(asnIS, length);
                break;
            default:
                throw new MAPParsingComponentException("Error while decoding " + _PrimitiveName
                        + ": Expexted imsi [0] IMSI or msisdn [1] ISDN-AddressString, but found " + asnIS.getTag(),
                        MAPParsingComponentExceptionReason.MistypedParameter);
        }
    }

    @Override
    public void decodeData(AsnInputStream ansIS, int length) throws MAPParsingComponentException {
        this._decode(ansIS, length);
    }

    @Override
    public void encodeAll(AsnOutputStream asnOs) throws MAPException {
        this.encodeAll(asnOs, this.getTagClass(), this.getTag());

    }

    @Override
    public void encodeAll(AsnOutputStream asnOs, int tagClass, int tag) throws MAPException {
        try {
            asnOs.writeTag(tagClass, this.getIsPrimitive(), tag);
            int pos = asnOs.StartContentDefiniteLength();
            this.encodeData(asnOs);
            asnOs.FinalizeContent(pos);
        } catch (AsnException e) {
            throw new MAPException("AsnException when encoding " + _PrimitiveName + ": " + e.getMessage(), e);
        }
    }

    @Override
    public void encodeData(AsnOutputStream asnOs) throws MAPException {
        if (this.imsi == null && this.tmsi == null)
            throw new MAPException("Error while encoding " + _PrimitiveName + ": all choices must not be null");
        if (this.imsi != null && this.tmsi != null)
            throw new MAPException("Error while encoding " + _PrimitiveName + ": all choices must not be not null");

        if (this.imsi != null) {
            ((IMSIImpl) this.imsi).encodeData(asnOs);
        } else {
            ((TMSIImpl) this.tmsi).encodeData(asnOs);
        }
    }

    @Override
    public String toString() {
        return "SubscriberIdImpl{" +
                "tmsi=" + tmsi +
                ", imsi=" + imsi +
                '}';
    }
}

ASN.1 Spec looks like this:

MAP V1: SendParametersArg ::= SEQUENCE { subscriberId SubscriberId, requestParameterList RequestParameterList}
requestParameterList SEQUENCE SIZE (1..2) OF RequestParameter
SubscriberId ::= CHOICE { imsi [0] IMSI, tmsi [1] TMSI}

Wireshark screenshot: enter image description here


Solution

  • After taking a look at wireshark source code dissectors for asn.1, it looks like they use SubscriberIdentity for SendParametersArg instead of SubscriberId. The difference is the first is a choice between IMSI and MSISDN while the latter is a choice between IMSI and TMSI.