| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527 |
- // Copyright 2019 Yunion
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package icmp6
- import (
- "fmt"
- "net"
- "github.com/google/gopacket"
- "github.com/google/gopacket/layers"
- "yunion.io/x/pkg/errors"
- )
- type TPreference int
- const (
- PreferenceLow TPreference = 0
- PreferenceMedium TPreference = 1
- PreferenceHigh TPreference = 2
- )
- func (pref TPreference) String() string {
- switch pref {
- case PreferenceLow:
- return "low"
- case PreferenceMedium:
- return "medium"
- case PreferenceHigh:
- return "high"
- }
- return "unknown"
- }
- type SBaseICMP6Message struct {
- SrcMac net.HardwareAddr
- SrcIP net.IP
- DstMac net.HardwareAddr
- DstIP net.IP
- Vlan uint16
- }
- func (msg SBaseICMP6Message) String() string {
- var vlanStr string
- if msg.Vlan > 1 {
- vlanStr += fmt.Sprintf("(vlan %d)", msg.Vlan)
- }
- return fmt.Sprintf("%s[%s]->%s[%s]%s", msg.SrcIP.String(), msg.SrcMac.String(), msg.DstIP.String(), msg.DstMac.String(), vlanStr)
- }
- type IICMP6Message interface {
- fmt.Stringer
- Payload() gopacket.SerializableLayer
- ICMP6TypeCode() layers.ICMPv6TypeCode
- SourceMac() net.HardwareAddr
- SourceIP() net.IP
- DestinationMac() net.HardwareAddr
- DestinationIP() net.IP
- VlanId() uint16
- }
- func (msg SBaseICMP6Message) SourceIP() net.IP {
- return msg.SrcIP
- }
- func (msg SBaseICMP6Message) SourceMac() net.HardwareAddr {
- return msg.SrcMac
- }
- func (msg SBaseICMP6Message) DestinationIP() net.IP {
- return msg.DstIP
- }
- func (msg SBaseICMP6Message) DestinationMac() net.HardwareAddr {
- return msg.DstMac
- }
- func (msg SBaseICMP6Message) VlanId() uint16 {
- return msg.Vlan
- }
- type SRouterSolicitation struct {
- SBaseICMP6Message
- }
- type SPrefixInfoOption struct {
- IsOnlink bool
- IsAutoconf bool
- Prefix net.IP
- PrefixLen uint8
- ValidLifetime uint32
- PreferredLifetime uint32
- }
- func (opt SPrefixInfoOption) String() string {
- return fmt.Sprintf("%s/%d (IsOnlink: %t, IsAutoconf: %t) ValidLifetime: %d, PreferredLifetime: %d", opt.Prefix.String(), opt.PrefixLen, opt.IsOnlink, opt.IsAutoconf, opt.ValidLifetime, opt.PreferredLifetime)
- }
- type SRouteInfoOption struct {
- RouteLifetime uint32
- Prefix net.IP
- PrefixLen uint8
- Preference TPreference
- }
- func (opt SRouteInfoOption) String() string {
- return fmt.Sprintf("%s/%d (Pref: %s) RouteLifetime: %d", opt.Prefix.String(), opt.PrefixLen, opt.Preference.String(), opt.RouteLifetime)
- }
- type SRouterAdvertisement struct {
- SBaseICMP6Message
- CurHopLimit uint8
- IsManaged bool
- IsOther bool
- IsHomeAgent bool
- Preference TPreference
- RouterLifetime uint16
- ReachableTime uint32
- RetransTimer uint32
- MTU uint32
- PrefixInfo []SPrefixInfoOption
- RouteInfo []SRouteInfoOption
- }
- type SNeighborSolicitation struct {
- SBaseICMP6Message
- /*
- The IP address of the target of the solicitation.
- It MUST NOT be a multicast address.
- */
- TargetAddr net.IP
- }
- type SNeighborAdvertisement struct {
- SBaseICMP6Message
- /*
- For solicited advertisements, the Target Address
- field in the Neighbor Solicitation message that
- prompted this advertisement. For an unsolicited
- advertisement, the address whose link-layer address
- has changed. The Target Address MUST NOT be a
- multicast address.
- */
- TargetAddr net.IP
- IsRouter bool
- IsSolicited bool
- }
- func (msg SNeighborSolicitation) ICMP6TypeCode() layers.ICMPv6TypeCode {
- return layers.CreateICMPv6TypeCode(layers.ICMPv6TypeNeighborSolicitation, 0)
- }
- func (msg SNeighborSolicitation) Payload() gopacket.SerializableLayer {
- pkt := layers.ICMPv6NeighborSolicitation{}
- pkt.TargetAddress = msg.TargetAddr
- pkt.Options = layers.ICMPv6Options{
- layers.ICMPv6Option{
- Type: layers.ICMPv6OptSourceAddress,
- Data: NewIcmpv6OptSourceTargetAddress(msg.SrcMac).Bytes(),
- },
- }
- return &pkt
- }
- func (msg *SNeighborSolicitation) Unmarshal(data *layers.ICMPv6NeighborSolicitation) error {
- msg.TargetAddr = data.TargetAddress
- return nil
- }
- func (msg SNeighborSolicitation) String() string {
- return fmt.Sprintf("NeighborSolicitation %s target %s", msg.SBaseICMP6Message.String(), msg.TargetAddr.String())
- }
- func (msg SNeighborAdvertisement) ICMP6TypeCode() layers.ICMPv6TypeCode {
- return layers.CreateICMPv6TypeCode(layers.ICMPv6TypeNeighborAdvertisement, 0)
- }
- func (msg SNeighborAdvertisement) Payload() gopacket.SerializableLayer {
- pkt := layers.ICMPv6NeighborAdvertisement{}
- pkt.TargetAddress = msg.TargetAddr
- pkt.Flags = 0x00
- if msg.IsRouter {
- pkt.Flags |= 0x80
- }
- if msg.IsSolicited {
- pkt.Flags |= 0x40
- }
- pkt.Options = layers.ICMPv6Options{
- layers.ICMPv6Option{
- Type: layers.ICMPv6OptSourceAddress,
- Data: NewIcmpv6OptSourceTargetAddress(msg.SrcMac).Bytes(),
- },
- }
- return &pkt
- }
- func (msg *SNeighborAdvertisement) Unmarshal(data *layers.ICMPv6NeighborAdvertisement) error {
- msg.TargetAddr = data.TargetAddress
- msg.IsRouter = data.Flags&0x80 != 0
- msg.IsSolicited = data.Flags&0x40 != 0
- return nil
- }
- func (msg SNeighborAdvertisement) String() string {
- return fmt.Sprintf("NeighborAdvertisement %s target %s", msg.SBaseICMP6Message.String(), msg.TargetAddr.String())
- }
- func (msg SRouterSolicitation) ICMP6TypeCode() layers.ICMPv6TypeCode {
- return layers.CreateICMPv6TypeCode(layers.ICMPv6TypeRouterSolicitation, 0)
- }
- func (msg SRouterSolicitation) Payload() gopacket.SerializableLayer {
- pkt := layers.ICMPv6RouterSolicitation{}
- pkt.Options = layers.ICMPv6Options{
- layers.ICMPv6Option{
- Type: layers.ICMPv6OptSourceAddress,
- Data: NewIcmpv6OptSourceTargetAddress(msg.SrcMac).Bytes(),
- },
- }
- return &pkt
- }
- func (msg *SRouterSolicitation) Unmarshal(data *layers.ICMPv6RouterSolicitation) error {
- return nil
- }
- func (msg SRouterSolicitation) String() string {
- return fmt.Sprintf("RouterSolicitation %s", msg.SBaseICMP6Message.String())
- }
- func (msg SRouterAdvertisement) ICMP6TypeCode() layers.ICMPv6TypeCode {
- return layers.CreateICMPv6TypeCode(layers.ICMPv6TypeRouterAdvertisement, 0)
- }
- func (msg SRouterAdvertisement) Payload() gopacket.SerializableLayer {
- pkt := layers.ICMPv6RouterAdvertisement{}
- // https://datatracker.ietf.org/doc/html/rfc4861#section-4.2
- // https://datatracker.ietf.org/doc/html/rfc4191
- pkt.Flags = 0x00 // M=1, O=1, stateful DHCPv6, pref=00
- if msg.IsManaged {
- pkt.Flags |= 0x80
- }
- if msg.IsOther {
- pkt.Flags |= 0x40
- }
- if msg.IsHomeAgent {
- pkt.Flags |= 0x20
- }
- switch msg.Preference {
- case PreferenceLow:
- pkt.Flags |= 0x18
- case PreferenceMedium:
- // do nothing
- case PreferenceHigh:
- pkt.Flags |= 0x08
- }
- pkt.HopLimit = msg.CurHopLimit
- pkt.RouterLifetime = msg.RouterLifetime
- pkt.ReachableTime = msg.ReachableTime
- pkt.RetransTimer = msg.RetransTimer
- pkt.Options = layers.ICMPv6Options{}
- for i := range msg.PrefixInfo {
- pref := msg.PrefixInfo[i]
- pkt.Options = append(pkt.Options, layers.ICMPv6Option{
- Type: layers.ICMPv6OptPrefixInfo,
- Data: NewIcmpv6OptPrefixInfo(pref).Bytes(),
- })
- }
- for i := range msg.RouteInfo {
- rt := msg.RouteInfo[i]
- pkt.Options = append(pkt.Options, layers.ICMPv6Option{
- Type: 24,
- Data: NewIcmpv6OptRouteInfo(rt).Bytes(),
- })
- }
- if msg.MTU > 0 {
- pkt.Options = append(pkt.Options, layers.ICMPv6Option{
- Type: layers.ICMPv6OptMTU,
- Data: NewIcmpV6OptMtu(msg.MTU).Bytes(),
- })
- }
- pkt.Options = append(pkt.Options, layers.ICMPv6Option{
- Type: layers.ICMPv6OptSourceAddress,
- Data: NewIcmpv6OptSourceTargetAddress(msg.SrcMac).Bytes(),
- })
- return &pkt
- }
- func (msg *SRouterAdvertisement) Unmarshal(data *layers.ICMPv6RouterAdvertisement) error {
- msg.CurHopLimit = data.HopLimit
- msg.IsManaged = data.Flags&0x80 != 0
- msg.IsOther = data.Flags&0x40 != 0
- msg.IsHomeAgent = data.Flags&0x20 != 0
- switch data.Flags & 0x18 {
- case 0x18:
- msg.Preference = PreferenceLow
- case 0x08:
- msg.Preference = PreferenceHigh
- default:
- msg.Preference = PreferenceMedium
- }
- msg.RouterLifetime = data.RouterLifetime
- for i := range data.Options {
- opt := data.Options[i]
- switch opt.Type {
- case layers.ICMPv6OptMTU:
- msg.MTU = DecodeIcmpV6OptMtu(opt.Data)
- case layers.ICMPv6OptPrefixInfo:
- prefix := DecodeIcmpv6OptPrefixInfo(opt.Data)
- msg.PrefixInfo = append(msg.PrefixInfo, prefix)
- case 24:
- route := DecodeIcmpv6OptRouteInfo(opt.Data)
- msg.RouteInfo = append(msg.RouteInfo, route)
- }
- }
- return nil
- }
- func (msg SRouterAdvertisement) String() string {
- return fmt.Sprintf(`RouterAdvertisement %s
- CurHopLimit: %d, IsManaged: %t, IsOther: %t, IsHomeAgent: %t, Preference: %s, RouterLifetime: %d, ReachableTime: %d, RetransTimer: %d
- MTU: %d
- PrefixInfo: %v
- RouteInfo: %v)`,
- msg.SBaseICMP6Message.String(),
- msg.CurHopLimit, msg.IsManaged, msg.IsOther, msg.IsHomeAgent, msg.Preference.String(), msg.RouterLifetime, msg.ReachableTime, msg.RetransTimer,
- msg.MTU, msg.PrefixInfo, msg.RouteInfo)
- }
- func EncodePacket(msg IICMP6Message) ([]byte, error) {
- var pktLayers []gopacket.SerializableLayer
- var eth = &layers.Ethernet{
- EthernetType: layers.EthernetTypeIPv6,
- SrcMAC: msg.SourceMac(),
- DstMAC: msg.DestinationMac(),
- }
- pktLayers = append(pktLayers, eth)
- if msg.VlanId() > 1 {
- eth.EthernetType = layers.EthernetTypeDot1Q
- dot1Q := &layers.Dot1Q{
- VLANIdentifier: msg.VlanId(),
- Priority: 6,
- Type: layers.EthernetTypeIPv6,
- }
- pktLayers = append(pktLayers, dot1Q)
- }
- var ip = &layers.IPv6{
- Version: 6,
- HopLimit: 0xff,
- TrafficClass: 0xc0,
- SrcIP: msg.SourceIP(),
- DstIP: msg.DestinationIP(),
- NextHeader: layers.IPProtocolICMPv6,
- }
- pktLayers = append(pktLayers, ip)
- var icmp6 = &layers.ICMPv6{
- TypeCode: msg.ICMP6TypeCode(),
- }
- icmp6.SetNetworkLayerForChecksum(ip)
- pktLayers = append(pktLayers, icmp6)
- pktLayers = append(pktLayers, msg.Payload())
- var (
- buf = gopacket.NewSerializeBuffer()
- opts = gopacket.SerializeOptions{ComputeChecksums: true, FixLengths: true}
- )
- if err := gopacket.SerializeLayers(buf, opts, pktLayers...); err != nil {
- return nil, errors.Wrap(err, "SerializeLayers error")
- }
- return buf.Bytes(), nil
- }
- func DecodePacket(data []byte) (IICMP6Message, error) {
- packet := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.Default)
- if packet.ErrorLayer() != nil {
- return nil, errors.Wrap(packet.ErrorLayer().Error(), "DecodePacket error")
- }
- var baseMsg SBaseICMP6Message
- {
- ethLayer := packet.Layer(layers.LayerTypeEthernet)
- if ethLayer != nil {
- eth := ethLayer.(*layers.Ethernet)
- baseMsg.SrcMac = eth.SrcMAC
- baseMsg.DstMac = eth.DstMAC
- } else {
- return nil, errors.Wrap(packet.ErrorLayer().Error(), "Expect Ethernet layer")
- }
- }
- {
- // optional vlan layer
- dot1qLayer := packet.Layer(layers.LayerTypeDot1Q)
- if dot1qLayer != nil {
- dot1q := dot1qLayer.(*layers.Dot1Q)
- baseMsg.Vlan = dot1q.VLANIdentifier
- }
- }
- {
- ipLayer := packet.Layer(layers.LayerTypeIPv6)
- if ipLayer != nil {
- // ipv6
- ip6 := ipLayer.(*layers.IPv6)
- baseMsg.SrcIP = ip6.SrcIP
- baseMsg.DstIP = ip6.DstIP
- } else {
- return nil, errors.Wrap(packet.ErrorLayer().Error(), "Expect IPv6 packet")
- }
- }
- {
- icmpLayer := packet.Layer(layers.LayerTypeICMPv6)
- if icmpLayer != nil {
- icmp6 := icmpLayer.(*layers.ICMPv6)
- if icmp6 == nil {
- return nil, errors.Wrap(packet.ErrorLayer().Error(), "Expect IPv6 packet")
- }
- switch icmp6.TypeCode.Type() {
- case 133:
- payload := packet.Layer(layers.LayerTypeICMPv6RouterSolicitation)
- if payload != nil {
- msg := &SRouterSolicitation{
- SBaseICMP6Message: baseMsg,
- }
- err := msg.Unmarshal(payload.(*layers.ICMPv6RouterSolicitation))
- if err != nil {
- return nil, errors.Wrap(err, "Unmarshal Router Solicitation packet")
- }
- return msg, nil
- } else {
- return nil, errors.Wrap(packet.ErrorLayer().Error(), "Expect ICMPv6 Router Solicitation packet")
- }
- case 134:
- payload := packet.Layer(layers.LayerTypeICMPv6RouterAdvertisement)
- if payload != nil {
- msg := &SRouterAdvertisement{
- SBaseICMP6Message: baseMsg,
- }
- err := msg.Unmarshal(payload.(*layers.ICMPv6RouterAdvertisement))
- if err != nil {
- return nil, errors.Wrap(err, "Unmarshal Router Advertisement packet")
- }
- return msg, nil
- } else {
- return nil, errors.Wrap(packet.ErrorLayer().Error(), "Expect ICMPv6 Router Advertisement packet")
- }
- case 135:
- payload := packet.Layer(layers.LayerTypeICMPv6NeighborSolicitation)
- if payload != nil {
- msg := &SNeighborSolicitation{
- SBaseICMP6Message: baseMsg,
- }
- err := msg.Unmarshal(payload.(*layers.ICMPv6NeighborSolicitation))
- if err != nil {
- return nil, errors.Wrap(err, "Unmarshal Neighbor Solicitation packet")
- }
- return msg, nil
- } else {
- return nil, errors.Wrap(packet.ErrorLayer().Error(), "Expect ICMPv6 Neighbor Solicitation packet")
- }
- case 136:
- payload := packet.Layer(layers.LayerTypeICMPv6NeighborAdvertisement)
- if payload != nil {
- msg := &SNeighborAdvertisement{
- SBaseICMP6Message: baseMsg,
- }
- err := msg.Unmarshal(payload.(*layers.ICMPv6NeighborAdvertisement))
- if err != nil {
- return nil, errors.Wrap(err, "Unmarshal Neighbor Advertisement packet")
- }
- return msg, nil
- } else {
- return nil, errors.Wrap(packet.ErrorLayer().Error(), "Expect ICMPv6 Neighbor Advertisement packet")
- }
- default:
- return nil, errors.Wrap(packet.ErrorLayer().Error(), "Expect ICMPv6 packet")
- }
- } else {
- return nil, errors.Wrap(packet.ErrorLayer().Error(), "Expect IPv6 packet")
- }
- }
- }
|