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}
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.