func fragment(eth layers.Ethernet, ip layers.IPv4, pmtu int, frame *ForwardedFrame, forward func(*ForwardedFrame)) error { // We are not doing any sort of NAT, so we don't need to worry // about checksums of IP payload (eg UDP checksum). headerSize := int(ip.IHL) * 4 // &^ is bit clear (AND NOT). So here we're clearing the lowest 3 // bits. maxSegmentSize := (pmtu - headerSize) &^ 7 opts := gopacket.SerializeOptions{ FixLengths: false, ComputeChecksums: true} payloadSize := int(ip.Length) - headerSize payload := ip.BaseLayer.Payload[:payloadSize] offsetBase := int(ip.FragOffset) << 3 origFlags := ip.Flags ip.Flags = ip.Flags | layers.IPv4MoreFragments ip.Length = uint16(headerSize + maxSegmentSize) if eth.EthernetType == layers.EthernetTypeLLC { // using LLC, so must set eth length correctly. eth length // is just the length of the payload eth.Length = ip.Length } else { eth.Length = 0 } for offset := 0; offset < payloadSize; offset += maxSegmentSize { var segmentPayload []byte if len(payload) <= maxSegmentSize { // last one segmentPayload = payload ip.Length = uint16(len(payload) + headerSize) ip.Flags = origFlags if eth.EthernetType == layers.EthernetTypeLLC { eth.Length = ip.Length } else { eth.Length = 0 } } else { segmentPayload = payload[:maxSegmentSize] payload = payload[maxSegmentSize:] } ip.FragOffset = uint16((offset + offsetBase) >> 3) buf := gopacket.NewSerializeBuffer() segPayload := gopacket.Payload(segmentPayload) err := gopacket.SerializeLayers(buf, opts, ð, &ip, &segPayload) if err != nil { return err } // make copies of the frame we received segFrame := *frame segFrame.frame = buf.Bytes() forward(&segFrame) } return nil }
/*Shim inserts the given router into the shim layer route record of the given IPv4 packet, creating a new route record if it's not already present.*/ func Shim(ipLayer *layers.IPv4, r Router) { ipPayload := bytes.NewBuffer(ipLayer.LayerPayload()) var modifiedIPPayload bytes.Buffer var rr RouteRecord if Shimmed(ipLayer) { rr.ReadFrom(ipPayload) ipLayer.Length -= uint16(rr.Len()) } else { rr.Protocol = uint8(ipLayer.Protocol) } /*Add the specified router to the route record and put the record at the beginning of the payload.*/ rr.AddRouter(r) rr.WriteTo(&modifiedIPPayload) ipPayload.WriteTo(&modifiedIPPayload) ipLayer.Length += uint16(rr.Len()) ipLayer.Protocol = layers.IPProtocol(IPProtocolAITFRouteRecord) ipLayer.Checksum = 0 ipLayer.Payload = modifiedIPPayload.Bytes() }
/*Unshim removes the shim layer from an IPv4 packet, if it's present.*/ func Unshim(ipLayer *layers.IPv4) *RouteRecord { if Shimmed(ipLayer) { /*Remove the route record from the payload*/ ipPayload := bytes.NewBuffer(ipLayer.LayerPayload()) var rr RouteRecord rr.ReadFrom(ipPayload) ipLayer.Length -= uint16(rr.Len()) ipLayer.Protocol = layers.IPProtocol(rr.Protocol) ipLayer.Checksum = 0 ipLayer.Payload = ipPayload.Bytes() return &rr } return nil }