看看我写的DSL(1)-数据包创建

fineqtbull 2009-11-14
正在用Scala开发一个开源协议测试软件,准备尝试一下DSL的开发,在这里晒晒阶段开发成果。学Scala开发不久,不保证这是DSL的最好设计,仅供大家参考和讨论。
//创建数据包(ICMP Echo Request)
val pingRequest = ^^^("EtherProtocol") {
	^^("EtherProtocol") {
		^("dstAddress") = AS("11:22:33:44:55:66", MAC_ADDRESS)
		^("srcAddress") = AS("22:22:33:44:55:66", MAC_ADDRESS)
		^("etherType") = 0x0800
		^("payload") {
			^("ipv4") {
				^("header") {
					^("identification") = 1
					^("timeToLive") = 255
					^("protocol") = 1
					^("sourceAddress") = AS("192.168.1.1", IPV4_ADDRESS)
					^("destinationAddress") = AS("192.168.1.2", IPV4_ADDRESS)
					^("options") {
						^(0) {
							^("security") {
								^("security" ) = 0x789A
							}
						}
						^(1) {
							^("lssr") {
								^("pointer" ) = 100
							}
						}
					}
				}
				^("payload") {
					^("icmpv4") {
						^("echoRequest") {
							^("identifier") = 100
							^("sequenceNumber") = 1000
							^("data") = OS("01020304")
						}
					}
				}
			}
		}
	}
}
//打印
ValueDumper.dump(pingRequest)

执行结果:
0:66  EtherProtocol.EtherProtocol 
0:6     dstAddress           11:22:33:44:55:66 
6:6     srcAddress           22:22:33:44:55:66 
12:2    etherType            2048 IPv4
14:48   payload              
14:48     ipv4               Ipv4Protocol.Ipv4Protocol
14:48 Ipv4Protocol.Ipv4Protocol 
14:36   header               
14:1      0000----           4 version 0xF0 
14:1      ----0000           0 headerLength 0x0F 
15:1      typeOfService      '00000000'B 
16:2      totalLength        0 
18:2      identification     1 
20:1      0---------------   0 reserved 0x8000 
20:1      -0--------------   false doNotFrag 0x4000 
20:1      --0-------------   false moreFrags 0x2000 
20:2      ---0000000000000   0 fragmentOffset 0x1FFF 
22:1      timeToLive         255 
23:1      protocol           1 ICMP: Internet Control Message Protocol
24:2      hcs                '0000'O 
26:4      sourceAddress      192.168.1.1 
30:4      destinationAddress 192.168.1.2 
34:16     options            
34:12       [0]              
34:12         security       
34:1            optionType   130 
35:1            optionLength 0 
36:2            security     30874 EFTO
38:2            compartments 0 
40:2            handling     0 
42:3            tcc          0 
45:1            nop          1 
46:4        [1]              
46:4          lssr           
46:1            optionType   131 
47:1            optionLength 0 
48:1            pointer      100 
49:0            routeData    
49:1            nop          1 
50:12   payload              
50:12     icmpv4             Icmpv4Protocol.Icmpv4Protocol
50:12 Icmpv4Protocol.Icmpv4Protocol 
50:12   echoRequest          
50:1      icmpType           8 
51:1      icmpCode           0 
52:2      checksum           '0000'O 
54:2      identifier         100 
56:2      sequenceNumber     1000 
58:4      data               '01020304'O 
62:4    fcs                  '00000000'O
night_stalker 2009-11-14
这些 ^ 太奇怪了 …… 还比不上老老实实赋值简洁平白 ……

val pingRequest = new EtherProtocol() {
  dstAddress = MAC("11:22:33:44:55:66")
  srcAddress = MAC("22:22:33:44:55:66")
  etherType  = 0x0800
  payload    = new Ipv4() {
    ...
  }
}
fineqtbull 2009-11-14
night_stalker 写道
这些 ^ 太奇怪了 …… 还比不上老老实实赋值简洁平白 ……

val pingRequest = new EtherProtocol() {
  dstAddress = MAC("11:22:33:44:55:66")
  srcAddress = MAC("22:22:33:44:55:66")
  etherType  = 0x0800
  payload    = new Ipv4() {
    ...
  }
}

,这种方案我也考虑过,而且在性能上也会好一点,还可以靠编译器来检查字段名称正确与否,不过要另外生成Scala类,所以暂时还不会实现。直接用^("字段名")格式是由于协议格式已经以别的文本形式定义了,所以不用生成Scala类直接用字段名就可以了,觉得会方便一点。下面是Ethernet协议的定义:
module EtherProtocol {
    import from BasicTypeAndValues {
        type Oct6, UInt16, Oct4, MacAddress
    }
    import from Ipv4Protocol {
        type Ipv4Protocol
    }
    import from ArpProtocol {
        type ArpProtocol
    }
    import from GlobalEnumSets {
        enumset EtherTypes
    }
    import from Ipv6Protocol {
        type Ipv6Protocol
    }
    
    type record EtherProtocol {
        MacAddress dstAddress,
        MacAddress srcAddress,
        UInt16 etherType,
        EtherPayload payload,
        Oct4 fcs optional
    } with {
        variant Protocol(true); PushEmptyField([DECODE], "etherType")
        variant(etherType) SetField([DECODE])
        enumref(etherType) EtherTypes
    }
    
    type union EtherPayload {
        Ipv4Protocol ipv4,
        ArpProtocol arp,
        Ipv6Protocol ipv6,
        octetstring data
    } with {
        variant CaseRef("-etherType", #UInt16); CaseDefault(data)
        variant(ipv4) CaseCond(UInt16 (0x0800))
        variant(arp) CaseCond(UInt16 (0x0806))
        variant(ipv6) CaseCond(UInt16 (0x86DD))
    }
    
} with {
    encode "FPB"
    variant ByteOrder(BIG_ENDIAN)
}

PS:本来想用#的,不过#是关键字所以选了^。另外^^^、^^、^分别表示模块、类型、字段。
matt.u 2009-11-17
^^^太不直观了。
fineqtbull 2009-11-17
matt.u 写道
^^^太不直观了。

主要是考虑到模块、类型、字段的层次关系,字段用得最多,如果用“field”等字符输入会挺麻烦,所以用了一个符号^。而根据层次关系,由此得来了^^和^^^。上来可能有点怪....
Global site tag (gtag.js) - Google Analytics