github.com/Andyfoo/golang/x/net@v0.0.0-20190901054642-57c1bf301704/icmp/multipart.go (about)

     1  // Copyright 2015 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package icmp
     6  
     7  import "github.com/Andyfoo/golang/x/net/internal/iana"
     8  
     9  // multipartMessageBodyDataLen takes b as an original datagram and
    10  // exts as extensions, and returns a required length for message body
    11  // and a required length for a padded original datagram in wire
    12  // format.
    13  func multipartMessageBodyDataLen(proto int, withOrigDgram bool, b []byte, exts []Extension) (bodyLen, dataLen int) {
    14  	bodyLen = 4 // length of leading octets
    15  	var extLen int
    16  	var rawExt bool // raw extension may contain an empty object
    17  	for _, ext := range exts {
    18  		extLen += ext.Len(proto)
    19  		if _, ok := ext.(*RawExtension); ok {
    20  			rawExt = true
    21  		}
    22  	}
    23  	if extLen > 0 && withOrigDgram {
    24  		dataLen = multipartMessageOrigDatagramLen(proto, b)
    25  	} else {
    26  		dataLen = len(b)
    27  	}
    28  	if extLen > 0 || rawExt {
    29  		bodyLen += 4 // length of extension header
    30  	}
    31  	bodyLen += dataLen + extLen
    32  	return bodyLen, dataLen
    33  }
    34  
    35  // multipartMessageOrigDatagramLen takes b as an original datagram,
    36  // and returns a required length for a padded orignal datagram in wire
    37  // format.
    38  func multipartMessageOrigDatagramLen(proto int, b []byte) int {
    39  	roundup := func(b []byte, align int) int {
    40  		// According to RFC 4884, the padded original datagram
    41  		// field must contain at least 128 octets.
    42  		if len(b) < 128 {
    43  			return 128
    44  		}
    45  		r := len(b)
    46  		return (r + align - 1) &^ (align - 1)
    47  	}
    48  	switch proto {
    49  	case iana.ProtocolICMP:
    50  		return roundup(b, 4)
    51  	case iana.ProtocolIPv6ICMP:
    52  		return roundup(b, 8)
    53  	default:
    54  		return len(b)
    55  	}
    56  }
    57  
    58  // marshalMultipartMessageBody takes data as an original datagram and
    59  // exts as extesnsions, and returns a binary encoding of message body.
    60  // It can be used for non-multipart message bodies when exts is nil.
    61  func marshalMultipartMessageBody(proto int, withOrigDgram bool, data []byte, exts []Extension) ([]byte, error) {
    62  	bodyLen, dataLen := multipartMessageBodyDataLen(proto, withOrigDgram, data, exts)
    63  	b := make([]byte, bodyLen)
    64  	copy(b[4:], data)
    65  	if len(exts) > 0 {
    66  		b[4+dataLen] = byte(extensionVersion << 4)
    67  		off := 4 + dataLen + 4 // leading octets, data, extension header
    68  		for _, ext := range exts {
    69  			switch ext := ext.(type) {
    70  			case *MPLSLabelStack:
    71  				if err := ext.marshal(proto, b[off:]); err != nil {
    72  					return nil, err
    73  				}
    74  				off += ext.Len(proto)
    75  			case *InterfaceInfo:
    76  				attrs, l := ext.attrsAndLen(proto)
    77  				if err := ext.marshal(proto, b[off:], attrs, l); err != nil {
    78  					return nil, err
    79  				}
    80  				off += ext.Len(proto)
    81  			case *InterfaceIdent:
    82  				if err := ext.marshal(proto, b[off:]); err != nil {
    83  					return nil, err
    84  				}
    85  				off += ext.Len(proto)
    86  			case *RawExtension:
    87  				copy(b[off:], ext.Data)
    88  				off += ext.Len(proto)
    89  			}
    90  		}
    91  		s := checksum(b[4+dataLen:])
    92  		b[4+dataLen+2] ^= byte(s)
    93  		b[4+dataLen+3] ^= byte(s >> 8)
    94  		if withOrigDgram {
    95  			switch proto {
    96  			case iana.ProtocolICMP:
    97  				b[1] = byte(dataLen / 4)
    98  			case iana.ProtocolIPv6ICMP:
    99  				b[0] = byte(dataLen / 8)
   100  			}
   101  		}
   102  	}
   103  	return b, nil
   104  }
   105  
   106  // parseMultipartMessageBody parses b as either a non-multipart
   107  // message body or a multipart message body.
   108  func parseMultipartMessageBody(proto int, typ Type, b []byte) ([]byte, []Extension, error) {
   109  	var l int
   110  	switch proto {
   111  	case iana.ProtocolICMP:
   112  		l = 4 * int(b[1])
   113  	case iana.ProtocolIPv6ICMP:
   114  		l = 8 * int(b[0])
   115  	}
   116  	if len(b) == 4 {
   117  		return nil, nil, nil
   118  	}
   119  	exts, l, err := parseExtensions(typ, b[4:], l)
   120  	if err != nil {
   121  		l = len(b) - 4
   122  	}
   123  	var data []byte
   124  	if l > 0 {
   125  		data = make([]byte, l)
   126  		copy(data, b[4:])
   127  	}
   128  	return data, exts, nil
   129  }