Skip to content

Commit

Permalink
add source addr, source port support
Browse files Browse the repository at this point in the history
  • Loading branch information
hujun-open committed Oct 14, 2024
1 parent ffad016 commit 8ed4c27
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 38 deletions.
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ dhcplt is a DHCPv4/DHCPv6 load tester for Linux with following features:
- DHCPv4

- Support DORA, Release, Renew and Rebind
- source addr, port could be customized
- Following DHCPv4 options could be included in request:
- Client Id
- Vendor Class
Expand All @@ -18,6 +19,7 @@ dhcplt is a DHCPv4/DHCPv6 load tester for Linux with following features:
- DHCPv6:

- Support DORA Release, Renew and Rebind
- source addr, port could be customized
- Request for IA_NA and/or IA_PD prefix
- Send request in relay-forward message to simulate a relayed message, and handle the relay-reply message
- following DHCPv6 options could be included in request:
Expand Down Expand Up @@ -132,7 +134,7 @@ Avg dial success time:135.940204ms
## Command Line Parameters

```
a DHCP load tester, v0.7.0
a DHCP load tester, unversioned
- action: dora | release | renew | rebind
default:dora
- applylease: apply assigned address on the interface if true
Expand All @@ -154,7 +156,7 @@ a DHCP load tester, v0.7.0
default:0
- flapstaydowndur: duriation of stay down
default:10s
- giaddr: Gi address for DHCPv4
- giaddr: Gi address for DHCPv4, simulating relay agent
default:0.0.0.0
- i: interface name
- interval: interval between setup of sessions
Expand All @@ -167,7 +169,7 @@ a DHCP load tester, v0.7.0
- n: number of clients
default:1
- needna: request DHCPv6 IANA if true
default:false
default:true
- needpd: request DHCPv6 IAPD if true
default:false
- profiling: enable profiling, dev use only
Expand All @@ -179,6 +181,14 @@ a DHCP load tester, v0.7.0
default:false
- sendrsfirst: send Router Solict first if true
default:false
- srcv4: source address for DHCPv4
default:0.0.0.0
- srcv4port: source port for egress DHCPv4 message
default:68
- srcv6: source address for DHCPv6
default:::
- srcv6port: source port for egress DHCPv6 message
default:546
- stackdelay: delay between setup v4 and v6, postive value means setup v4 first, negative means v6 first
default:0s
- timeout: setup timout
Expand Down Expand Up @@ -211,6 +221,8 @@ a DHCP load tester, v0.7.0
- flapnum: the number of clients flapping
- flapmaxinterval, flapmininterval: the duration a flapping client stay connected, it is random value between min and max
- flapstaydowndur: the duration a flapping client stay disconnected.
- srcv4port: by default source port is 68, 67 if giaddr is specified; however it could overriden by this parameter


### Config File
Thanks to [shouchan](https://github.com/hujun-open/shouchan), beside using CLI parameters, a YAML config file could also be used via "-f <conf_file>", the content of YAML is the `testSetup` struct
82 changes: 58 additions & 24 deletions conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,28 +40,32 @@ type testSetup struct {
ApplyLease bool `usage:"apply assigned address on the interface if true"`
Retry uint `usage:"number of setup retry"`
Timeout time.Duration `usage:"setup timout"`
GiAddr netip.Addr `usage:"Gi address for DHCPv4"`
GiAddr netip.Addr `usage:"Gi address for DHCPv4, simulating relay agent"`
SourceV4Addr netip.Addr `usage:"source address for DHCPv4" alias:"srcv4"`
SourceV6Port uint16 `usage:"source port for egress DHCPv6 message" alias:"srcv6port"`
SourceV4Port uint16 `usage:"source port for egress DHCPv4 message" alias:"srcv4port"`
//following are template str, $ID will be replaced by client id
RID string `usage:"BBF remote-id"`
CID string `usage:"BBF circuit-id"`
ClntID string `usage:"client-id"`
VendorClass string `usage:"vendor class"`
EnableV4 bool `alias:"v4" usage:"do DHCPv4 if true"`
//v6 specific
EnableV6 bool `alias:"v6" usage:"do DHCPv6 if true"`
StackDelay time.Duration `usage:"delay between setup v4 and v6, postive value means setup v4 first, negative means v6 first"`
V6MsgType dhcpv6.MessageType `usage:"DHCPv6 exchange type, solict|relay|auto"`
NeedNA bool `usage:"request DHCPv6 IANA if true"`
NeedPD bool `usage:"request DHCPv6 IAPD if true"`
pktRelay etherconn.PacketRelay
Driver etherconn.RelayType `usage:"etherconn forward engine"`
Flapping *FlappingConf `usage:"enable flapping"`
SendRSFirst bool `usage:"send Router Solict first if true"`
Profiling bool `usage:"enable profiling, dev use only"`
LeaseFile string
Action actionType `usage:"dora | release | renew | rebind"`
saveV4Chan chan *v4LeaseWithID
saveV6Chan chan *v6LeaseWithID
EnableV6 bool `alias:"v6" usage:"do DHCPv6 if true"`
SourceV6Addr netip.Addr `usage:"source address for DHCPv6" alias:"srcv6"`
StackDelay time.Duration `usage:"delay between setup v4 and v6, postive value means setup v4 first, negative means v6 first"`
V6MsgType dhcpv6.MessageType `usage:"DHCPv6 exchange type, solict|relay|auto"`
NeedNA bool `usage:"request DHCPv6 IANA if true"`
NeedPD bool `usage:"request DHCPv6 IAPD if true"`
pktRelay etherconn.PacketRelay
Driver etherconn.RelayType `usage:"etherconn forward engine"`
Flapping *FlappingConf `usage:"enable flapping"`
SendRSFirst bool `usage:"send Router Solict first if true"`
Profiling bool `usage:"enable profiling, dev use only"`
LeaseFile string
Action actionType `usage:"dora | release | renew | rebind"`
saveV4Chan chan *v4LeaseWithID
saveV6Chan chan *v6LeaseWithID
}

func newDefaultConf() *testSetup {
Expand All @@ -74,10 +78,15 @@ func newDefaultConf() *testSetup {
VLANStep: 1,
Interval: time.Second,
GiAddr: netip.MustParseAddr("0.0.0.0"),
SourceV4Addr: netip.MustParseAddr("0.0.0.0"),
SourceV6Addr: netip.MustParseAddr("::"),
Retry: 1,
Timeout: 5 * time.Second,
EnableV4: true,
EnableV6: false,
SourceV6Port: dhcpv6.DefaultClientPort,
SourceV4Port: dhcpv4.ClientPort,
NeedNA: true,
V6MsgType: dhcpv6.MessageTypeNone,
Driver: etherconn.RelayTypeAFP,
LeaseFile: "dhcplt.lease",
Expand Down Expand Up @@ -120,21 +129,46 @@ func (setup *testSetup) init() error {
if !setup.EnableV4 && !setup.EnableV6 {
return fmt.Errorf("both DHCPv4 and DHCPv6 are disabled")
}
if setup.SourceV4Port == 0 {
return fmt.Errorf("source v4 port can't be zero")
}
if setup.SourceV6Port == 0 {
return fmt.Errorf("source v6 port can't be zero")
}
if setup.EnableV4 {
if !setup.SourceV4Addr.IsUnspecified() {
if !setup.SourceV4Addr.Is4() || !setup.SourceV4Addr.IsGlobalUnicast() {
return fmt.Errorf("source v4 address must be an IPv4 unicast addr")
}
}
if setup.GiAddr.IsValid() {
if !setup.GiAddr.IsUnspecified() {
if !setup.GiAddr.Is4() || !setup.GiAddr.IsGlobalUnicast() {
return fmt.Errorf("gi address must be an IPv4 unicast addr")
}
}
} else {
setup.GiAddr = netip.MustParseAddr("0.0.0.0")
}
if setup.GiAddr.IsUnspecified() != setup.SourceV4Addr.IsUnspecified() {
fmt.Printf("warning: giaddr should be specified along with srcv4 address")
}
}
if setup.EnableV6 {
if !setup.SourceV6Addr.IsUnspecified() {
if !setup.SourceV6Addr.Is6() || !setup.SourceV6Addr.IsGlobalUnicast() {
return fmt.Errorf("source v6 address must be an IPv4 unicast addr")
}
}
}

if setup.NumOfClients == 0 {
return fmt.Errorf("number of client is 0")
}
for _, v := range setup.StartVLANs {
v.EtherType = uint16(setup.VLANEType)
}
if setup.GiAddr.IsValid() {
if !setup.GiAddr.IsUnspecified() {
if !setup.GiAddr.Is4() || !setup.GiAddr.IsGlobalUnicast() {
return fmt.Errorf("gi address must be an IPv4 unicast addr")
}
}
} else {
setup.GiAddr = netip.MustParseAddr("0.0.0.0")
}

setup.ExcludedVLANs = []uint16{}
for _, n := range setup.ExcludedVLANs {
if n > 4096 {
Expand Down
77 changes: 66 additions & 11 deletions sched.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,19 @@ func (dc *DClient) createV4OtherClnt(act actionType) error {
if dc.d4Lease == nil {
return fmt.Errorf("can't create v4 release client for %v without v4 lease", dc.id)
}
rudpconn, err := etherconn.NewRUDPConn(
myaddr.GenConnectionAddrStr("", dc.d4Lease.Lease.ACK.YourIPAddr, 68), dc.cfg.v4econn)
localPort := dhcpv4.ClientPort
if !dc.cfg.setup.GiAddr.IsUnspecified() {
localPort = dhcpv4.ServerPort
}
if dc.cfg.setup.SourceV4Port != dhcpv4.ClientPort {
localPort = int(dc.cfg.setup.SourceV4Port)
}

localaddr := myaddr.GenConnectionAddrStr("", dc.d4Lease.Lease.ACK.YourIPAddr, localPort)
if !dc.cfg.setup.SourceV4Addr.IsUnspecified() {
localaddr = myaddr.GenConnectionAddrStr("", dc.cfg.setup.SourceV4Addr.AsSlice(), localPort)
}
rudpconn, err := etherconn.NewRUDPConn(localaddr, dc.cfg.v4econn)
if err != nil {
return fmt.Errorf("failed to create raw udp conn for %v release,%v", dc.id, err)
}
Expand Down Expand Up @@ -133,16 +144,42 @@ func (dc *DClient) createV6OtherClnt() error {
return fmt.Errorf("can't create v6 release client for %v without v6 lease", dc.id)
}
// dc.d6ReleaseClnt = dc.d6
rudpconn, err := etherconn.NewRUDPConn(fmt.Sprintf("[%v]:%v",

localaddr := fmt.Sprintf("[%v]:%v",
myaddr.GetLLAFromMac(dc.d6Lease.MAC),
dhcpv6.DefaultClientPort), dc.cfg.v6econn, etherconn.WithAcceptAny(true))
dc.cfg.setup.SourceV6Port)
if !dc.cfg.setup.SourceV6Addr.IsUnspecified() {
localaddr = fmt.Sprintf("[%v]:%v",
dc.cfg.setup.SourceV6Addr,
dc.cfg.setup.SourceV6Port)
}
// var rudpconn *etherconn.RUDPConn
// var err error
rudpconn, err := etherconn.NewRUDPConn(localaddr, dc.cfg.v6econn, etherconn.WithAcceptAny(true))
if err != nil {
return fmt.Errorf("failed to create raw udp conn for %v release, %w", dc.id, err)
return fmt.Errorf("failed to create raw udp conn for %v for other actions, %w", dc.id, err)
}
dc.d6OtherClnt, err = nclient6.NewWithConn(rudpconn, dc.d6Lease.MAC)
if err != nil {
return fmt.Errorf("failed to create dhcp6 client %v for release, %w", dc.id, err)
switch dc.cfg.setup.V6MsgType {
case dhcpv6.MessageTypeSolicit:

dc.d6OtherClnt, err = nclient6.NewWithConn(rudpconn, dc.d6Lease.MAC)
if err != nil {
return fmt.Errorf("failed to create dhcp6 client %v for other actions, %w", dc.id, err)
}
case dhcpv6.MessageTypeRelayForward:
accessConClnt, accessConRelay := conpair.NewPacketConnPair()
dc.d6OtherClnt, err = nclient6.NewWithConn(accessConClnt, dc.d6Lease.MAC)
if err != nil {
return fmt.Errorf("failed to create dhcp6 client %v for for other actions, %w", dc.id, err)
}
dc.d6relay = dhcpv6relay.NewRelayAgent(context.Background(),
&dhcpv6relay.PairDHCPConn{PacketConnPair: accessConRelay},
&dhcpv6relay.RUDPDHCPConn{RUDPConn: rudpconn},
dhcpv6relay.WithLinkAddr(net.ParseIP("::")),
dhcpv6relay.WithPeerAddr(myaddr.GetLLAFromMac(dc.d6Lease.MAC)),
dhcpv6relay.WithOptions(dc.d6Lease.RelayIDOptions))
}

return nil
}

Expand Down Expand Up @@ -706,7 +743,18 @@ func NewSched(setup *testSetup) (*Sched, error) {
var key etherconn.L2EndpointKey
if dc.cfg.v4econn != nil {
key = dc.cfg.v4econn.LocalAddr().GetKey()
rudpconn, err := etherconn.NewRUDPConn("0.0.0.0:68", dc.cfg.v4econn,
localPort := dhcpv4.ClientPort
if !dc.cfg.setup.GiAddr.IsUnspecified() {
localPort = dhcpv4.ServerPort
}
if dc.cfg.setup.SourceV4Port != dhcpv4.ClientPort {
localPort = int(dc.cfg.setup.SourceV4Port)
}
localaddr := fmt.Sprintf("0.0.0.0:%d", localPort)
if !dc.cfg.setup.SourceV4Addr.IsUnspecified() {
localaddr = fmt.Sprintf("%v:%d", dc.cfg.setup.SourceV4Addr, localPort)
}
rudpconn, err := etherconn.NewRUDPConn(localaddr, dc.cfg.v4econn,
etherconn.WithAcceptAny(true))
if err != nil {
return nil, fmt.Errorf("failed to create raw udp conn for %v,%v", dc.cfg.Mac, err)
Expand Down Expand Up @@ -734,9 +782,16 @@ func NewSched(setup *testSetup) (*Sched, error) {
}

key = dc.cfg.v6econn.LocalAddr().GetKey()
rudpconn, err := etherconn.NewRUDPConn(fmt.Sprintf("[%v]:%v",

localaddr := fmt.Sprintf("[%v]:%v",
myaddr.GetLLAFromMac(dc.cfg.Mac),
dhcpv6.DefaultClientPort), dc.cfg.v6econn, etherconn.WithAcceptAny(true))
setup.SourceV6Port)
if !dc.cfg.setup.SourceV6Addr.IsUnspecified() {
localaddr = fmt.Sprintf("[%v]:%v",
dc.cfg.setup.SourceV6Addr,
setup.SourceV6Port)
}
rudpconn, err := etherconn.NewRUDPConn(localaddr, dc.cfg.v6econn, etherconn.WithAcceptAny(true))
if err != nil {
return nil, fmt.Errorf("failed to create raw udp conn for %v,%v", dc.cfg.Mac, err)
}
Expand Down

0 comments on commit 8ed4c27

Please sign in to comment.