mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-08-30 17:13:08 +08:00
78bf0bcb2f
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
528 lines
14 KiB
Go
528 lines
14 KiB
Go
// 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")
|
|
}
|
|
}
|
|
}
|