mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 21:15:39 +00:00
c1ef55249a
IPProto has been being converted to and from string formats in multiple locations with variations in behavior. TextMarshaller and JSONMarshaller implementations are now added, along with defined accepted and preferred formats to centralize the logic into a single cross compatible implementation. Updates tailscale/corp#15043 Fixes tailscale/corp#15141 Signed-off-by: James Tucker <james@tailscale.com>
200 lines
4.5 KiB
Go
200 lines
4.5 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
// Package ipproto contains IP Protocol constants.
|
|
package ipproto
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
|
|
"tailscale.com/util/nocasemaps"
|
|
"tailscale.com/util/vizerror"
|
|
)
|
|
|
|
// Version describes the IP address version.
|
|
type Version uint8
|
|
|
|
// Valid Version values.
|
|
const (
|
|
Version4 = 4
|
|
Version6 = 6
|
|
)
|
|
|
|
func (p Version) String() string {
|
|
switch p {
|
|
case Version4:
|
|
return "IPv4"
|
|
case Version6:
|
|
return "IPv6"
|
|
default:
|
|
return fmt.Sprintf("Version-%d", int(p))
|
|
}
|
|
}
|
|
|
|
// Proto is an IP subprotocol as defined by the IANA protocol
|
|
// numbers list
|
|
// (https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml),
|
|
// or the special values Unknown or Fragment.
|
|
type Proto uint8
|
|
|
|
const (
|
|
// Unknown represents an unknown or unsupported protocol; it's
|
|
// deliberately the zero value. Strictly speaking the zero
|
|
// value is IPv6 hop-by-hop extensions, but we don't support
|
|
// those, so this is still technically correct.
|
|
Unknown Proto = 0x00
|
|
|
|
// Values from the IANA registry.
|
|
ICMPv4 Proto = 0x01
|
|
IGMP Proto = 0x02
|
|
ICMPv6 Proto = 0x3a
|
|
TCP Proto = 0x06
|
|
UDP Proto = 0x11
|
|
DCCP Proto = 0x21
|
|
GRE Proto = 0x2f
|
|
SCTP Proto = 0x84
|
|
|
|
// TSMP is the Tailscale Message Protocol (our ICMP-ish
|
|
// thing), an IP protocol used only between Tailscale nodes
|
|
// (still encrypted by WireGuard) that communicates why things
|
|
// failed, etc.
|
|
//
|
|
// Proto number 99 is reserved for "any private encryption
|
|
// scheme". We never accept these from the host OS stack nor
|
|
// send them to the host network stack. It's only used between
|
|
// nodes.
|
|
TSMP Proto = 99
|
|
|
|
// Fragment represents any non-first IP fragment, for which we
|
|
// don't have the sub-protocol header (and therefore can't
|
|
// figure out what the sub-protocol is).
|
|
//
|
|
// 0xFF is reserved in the IANA registry, so we steal it for
|
|
// internal use.
|
|
Fragment Proto = 0xFF
|
|
)
|
|
|
|
// Deprecated: use MarshalText instead.
|
|
func (p Proto) String() string {
|
|
switch p {
|
|
case Unknown:
|
|
return "Unknown"
|
|
case Fragment:
|
|
return "Frag"
|
|
case ICMPv4:
|
|
return "ICMPv4"
|
|
case IGMP:
|
|
return "IGMP"
|
|
case ICMPv6:
|
|
return "ICMPv6"
|
|
case UDP:
|
|
return "UDP"
|
|
case TCP:
|
|
return "TCP"
|
|
case SCTP:
|
|
return "SCTP"
|
|
case TSMP:
|
|
return "TSMP"
|
|
case GRE:
|
|
return "GRE"
|
|
case DCCP:
|
|
return "DCCP"
|
|
default:
|
|
return fmt.Sprintf("IPProto-%d", int(p))
|
|
}
|
|
}
|
|
|
|
// Prefer names from
|
|
// https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
|
|
// unless otherwise noted.
|
|
var (
|
|
// preferredNames is the set of protocol names that re produced by
|
|
// MarshalText, and are the preferred representation.
|
|
preferredNames = map[Proto]string{
|
|
51: "ah",
|
|
DCCP: "dccp",
|
|
8: "egp",
|
|
50: "esp",
|
|
47: "gre",
|
|
ICMPv4: "icmp",
|
|
IGMP: "igmp",
|
|
9: "igp",
|
|
4: "ipv4",
|
|
ICMPv6: "ipv6-icmp",
|
|
SCTP: "sctp",
|
|
TCP: "tcp",
|
|
UDP: "udp",
|
|
}
|
|
|
|
// acceptedNames is the set of protocol names that are accepted by
|
|
// UnmarshalText.
|
|
acceptedNames = map[string]Proto{
|
|
"ah": 51,
|
|
"dccp": DCCP,
|
|
"egp": 8,
|
|
"esp": 50,
|
|
"gre": 47,
|
|
"icmp": ICMPv4,
|
|
"icmpv4": ICMPv4,
|
|
"icmpv6": ICMPv6,
|
|
"igmp": IGMP,
|
|
"igp": 9,
|
|
"ip-in-ip": 4, // IANA says "ipv4"; Wikipedia/popular use says "ip-in-ip"
|
|
"ipv4": 4,
|
|
"ipv6-icmp": ICMPv6,
|
|
"sctp": SCTP,
|
|
"tcp": TCP,
|
|
"tsmp": TSMP,
|
|
"udp": UDP,
|
|
}
|
|
)
|
|
|
|
// UnmarshalText implements encoding.TextUnmarshaler. If the input is empty, p
|
|
// is set to 0. If an error occurs, p is unchanged.
|
|
func (p *Proto) UnmarshalText(b []byte) error {
|
|
if len(b) == 0 {
|
|
*p = 0
|
|
return nil
|
|
}
|
|
|
|
if u, err := strconv.ParseUint(string(b), 10, 8); err == nil {
|
|
*p = Proto(u)
|
|
return nil
|
|
}
|
|
|
|
if newP, ok := nocasemaps.GetOk(acceptedNames, string(b)); ok {
|
|
*p = newP
|
|
return nil
|
|
}
|
|
|
|
return vizerror.Errorf("proto name %q not known; use protocol number 0-255", b)
|
|
}
|
|
|
|
// MarshalText implements encoding.TextMarshaler.
|
|
func (p Proto) MarshalText() ([]byte, error) {
|
|
if s, ok := preferredNames[p]; ok {
|
|
return []byte(s), nil
|
|
}
|
|
return []byte(strconv.Itoa(int(p))), nil
|
|
}
|
|
|
|
// MarshalJSON implements json.Marshaler.
|
|
func (p Proto) MarshalJSON() ([]byte, error) {
|
|
return []byte(strconv.Itoa(int(p))), nil
|
|
}
|
|
|
|
// UnmarshalJSON implements json.Unmarshaler. If the input is empty, p is set to
|
|
// 0. If an error occurs, p is unchanged. The input must be a JSON number or an
|
|
// accepted string name.
|
|
func (p *Proto) UnmarshalJSON(b []byte) error {
|
|
if len(b) == 0 {
|
|
*p = 0
|
|
return nil
|
|
}
|
|
if b[0] == '"' {
|
|
b = b[1 : len(b)-1]
|
|
}
|
|
return p.UnmarshalText(b)
|
|
}
|