<commit_before>package aero

import (
	"fmt"
	"io"
	"strings"

	"github.com/akyoto/color"
)

// node types
const (
	separator = '/'
	parameter = ':'
	wildcard  = '*'
)

// controlFlow tells the main loop what it should do next.
type controlFlow int

// controlFlow values.
const (
	controlStop  controlFlow = 0
	controlBegin controlFlow = 1
	controlNext  controlFlow = 2
)

// dataType specifies which type of data we are going to save for each node.
type dataType = Handler

// tree represents a radix tree.
type tree struct {
	prefix    string
	data      dataType
	children  [224]*tree
	parameter *tree
	wildcard  *tree
	kind      byte
}

// add adds a new element to the tree.
func (node *tree) add(path string, data dataType) {
	// Search tree for equal parts until we can no longer proceed
	i := 0
	offset := 0

	for {
	begin:
		switch node.kind {
		case parameter:
			// This only occurs when the same parameter based route is added twice.
			// node: /post/:id|
			// path: /post/:id|
			if i == len(path) {
				node.data = data
				return
			}

			// When we hit a separator, we'll search for a fitting child.
			if path[i] == separator {
				var control controlFlow
				node, offset, control = node.end(path, data, i, offset)

				switch control {
				case controlStop:
					return
				case controlBegin:
					goto begin
				case controlNext:
					goto next
				}
			}

		default:
			if i == len(path) {
				// The path already exists.
				// node: /blog|
				// path: /blog|
				if i-offset == len(node.prefix) {
					node.data = data
					return
				}

				// The path ended but the node prefix is longer.
				// node: /blog|feed
				// path: /blog|
				node.split(i-offset, "", data)
				return
			}

			// The node we just checked is entirely included in our path.
			// node: /|
			// path: /|blog
			if i-offset == len(node.prefix) {
				var control controlFlow
				node, offset, control = node.end(path, data, i, offset)

				switch control {
				case controlStop:
					return
				case controlBegin:
					goto begin
				case controlNext:
					goto next
				}
			}

			// We got a conflict.
			// node: /b|ag
			// path: /b|riefcase
			if path[i] != node.prefix[i-offset] {
				node.split(i-offset, path[i:], data)
				return
			}
		}

	next:
		i++
	}
}

// split splits the node at the given index and inserts
// a new child node with the given path and data.
// If path is empty, it will not create another child node
// and instead assign the data directly to the node.
func (node *tree) split(index int, path string, data dataType) {
	// Create split node with the remaining string
	splitNode := node.clone(node.prefix[index:])

	/// The existing data must be removed
	node.reset(node.prefix[:index])

	// If the path is empty, it means we don't create a 2nd child node.
	// Just assign the data for the existing node and store a single child node.
	if path == "" {
		node.data = data
		node.children[splitNode.prefix[0]-32] = splitNode
		return
	}

	node.children[splitNode.prefix[0]-32] = splitNode

	// Create new nodes with the remaining path
	node.append(path, data)
}

// clone clones the node with a new prefix.
func (node *tree) clone(prefix string) *tree {
	return &tree{
		prefix:    prefix,
		data:      node.data,
		children:  node.children,
		parameter: node.parameter,
		wildcard:  node.wildcard,
		kind:      node.kind,
	}
}

// reset resets the existing node data.
func (node *tree) reset(prefix string) {
	node.prefix = prefix
	node.data = nil
	node.parameter = nil
	node.wildcard = nil
	node.kind = 0
	node.children = [224]*tree{}
}

// addTrailingSlash adds a trailing slash with the same data.
func (node *tree) addTrailingSlash(data dataType) {
	if strings.HasSuffix(node.prefix, "/") || node.children[separator-32] != nil || node.kind == wildcard {
		return
	}

	node.children[separator-32] = &tree{
		prefix: "/",
		data:   data,
	}
}

// append appends the given path to the tree.
func (node *tree) append(path string, data dataType) {
	// At this point, all we know is that somewhere
	// in the remaining string we have parameters.
	// node: /user|
	// path: /user|/:userid
	for {
		if path == "" {
			node.data = data
			return
		}

		paramStart := strings.IndexByte(path, parameter)

		if paramStart == -1 {
			paramStart = strings.IndexByte(path, wildcard)
		}

		// If it's a static route we are adding,
		// just add the remainder as a normal node.
		if paramStart == -1 {
			// If the node itself doesn't have a prefix (root node),
			// don't add a child and use the node itself.
			if node.prefix == "" {
				node.prefix = path
				node.data = data
				return
			}

			child := &tree{
				prefix: path,
				data:   data,
			}

			node.children[path[0]-32] = child
			child.addTrailingSlash(data)
			return
		}

		// If we're directly in front of a parameter,
		// add a parameter node.
		if paramStart == 0 {
			paramEnd := strings.IndexByte(path, separator)

			if paramEnd == -1 {
				paramEnd = len(path)
			}

			child := &tree{
				prefix: path[1:paramEnd],
				kind:   path[paramStart],
			}

			switch child.kind {
			case parameter:
				child.addTrailingSlash(data)
				node.parameter = child
				node = child
				path = path[paramEnd:]
				continue

			case wildcard:
				child.data = data
				node.wildcard = child
				return
			}
		}

		// We know there's a parameter, but not directly at the start.

		// If the node itself doesn't have a prefix (root node),
		// don't add a child and use the node itself.
		if node.prefix == "" {
			node.prefix = path[:paramStart]
			path = path[paramStart:]
			continue
		}

		// Add a normal node with the path before the parameter start.
		child := &tree{
			prefix: path[:paramStart],
		}

		// Allow trailing slashes to return
		// the same content as their parent node.
		if child.prefix == "/" {
			child.data = node.data
		}

		node.children[path[0]-32] = child
		node = child
		path = path[paramStart:]
	}
}

// end is called when the node was fully parsed
// and needs to decide the next control flow.
func (node *tree) end(path string, data dataType, i int, offset int) (*tree, int, controlFlow) {
	child := node.children[path[i]-32]

	if child != nil {
		node = child
		offset = i
		return node, offset, controlNext
	}

	// No fitting children found, does this node even contain a prefix yet?
	// If no prefix is set, this is the starting node.
	if node.prefix == "" {
		node.append(path[i:], data)
		return node, offset, controlStop
	}

	// node: /user/|:id
	// path: /user/|:id/profile
	if node.parameter != nil {
		node = node.parameter
		offset = i
		return node, offset, controlBegin
	}

	node.append(path[i:], data)
	return node, offset, controlStop
}

// find finds the data for the given path and assigns it to ctx.handler, if available.
func (node *tree) find(path string, ctx *context) {
	var (
		i                  int
		offset             int
		lastWildcardOffset int
		lastWildcard       *tree
	)

	// Search tree for equal parts until we can no longer proceed
	for {
	begin:
		switch node.kind {
		case parameter:
			if i == len(path) {
				ctx.addParameter(node.prefix, path[offset:i])
				ctx.handler = node.data
				return
			}

			if path[i] == separator {
				ctx.addParameter(node.prefix, path[offset:i])
				node = node.children[separator-32]
				offset = i
				i++
				goto begin
			}

		default:
			// We reached the end.
			if i == len(path) {
				// node: /blog|
				// path: /blog|
				if i-offset == len(node.prefix) {
					ctx.handler = node.data
					return
				}

				// node: /blog|feed
				// path: /blog|
				ctx.handler = nil
				return
			}

			// The node we just checked is entirely included in our path.
			// node: /|
			// path: /|blog
			if i-offset == len(node.prefix) {
				if node.wildcard != nil {
					lastWildcard = node.wildcard
					lastWildcardOffset = i
				}

				child := node.children[path[i]-32]

				if child != nil {
					node = child
					offset = i
					i++
					goto begin
				}

				// node: /|:id
				// path: /|blog
				if node.parameter != nil {
					node = node.parameter
					offset = i
					goto begin
				}

				// node: /|*any
				// path: /|image.png
				if node.wildcard != nil {
					ctx.addParameter(node.wildcard.prefix, path[i:])
					ctx.handler = node.wildcard.data
					return
				}

				ctx.handler = nil
				return
			}

			// We got a conflict.
			// node: /b|ag
			// path: /b|riefcase
			if path[i] != node.prefix[i-offset] {
				if lastWildcard != nil {
					ctx.addParameter(lastWildcard.prefix, path[lastWildcardOffset:])
					ctx.handler = lastWildcard.data
					return
				}

				ctx.handler = nil
				return
			}
		}

		i++
	}
}

// each traverses the tree and calls the given function on every node.
func (node *tree) each(callback func(*tree)) {
	callback(node)

	for _, child := range node.children {
		if child == nil {
			continue
		}

		child.each(callback)
	}

	if node.parameter != nil {
		node.parameter.each(callback)
	}

	if node.wildcard != nil {
		node.wildcard.each(callback)
	}
}

// PrettyPrint prints a human-readable form of the tree to the given writer.
func (node *tree) PrettyPrint(writer io.Writer) {
	node.prettyPrint(writer, -1)
}

// prettyPrint
func (node *tree) prettyPrint(writer io.Writer, level int) {
	prefix := ""

	if level >= 0 {
		prefix = strings.Repeat("  ", level) + "|_ "
	}

	colorFunc := color.CyanString

	switch node.kind {
	case ':':
		prefix += ":"
		colorFunc = color.YellowString
	case '*':
		prefix += "*"
		colorFunc = color.GreenString
	}

	fmt.Fprintf(writer, "%s%s [%t]\n", prefix, colorFunc(node.prefix), node.data != nil)

	for _, child := range node.children {
		if child == nil {
			continue
		}

		child.prettyPrint(writer, level+1)
	}

	if node.parameter != nil {
		node.parameter.prettyPrint(writer, level+1)
	}

	if node.wildcard != nil {
		node.wildcard.prettyPrint(writer, level+1)
	}
}
<commit_msg>Use unsigned integers<commit_after>package aero

import (
	"fmt"
	"io"
	"strings"

	"github.com/akyoto/color"
)

// node types
const (
	separator = '/'
	parameter = ':'
	wildcard  = '*'
)

// controlFlow tells the main loop what it should do next.
type controlFlow int

// controlFlow values.
const (
	controlStop  controlFlow = 0
	controlBegin controlFlow = 1
	controlNext  controlFlow = 2
)

// dataType specifies which type of data we are going to save for each node.
type dataType = Handler

// tree represents a radix tree.
type tree struct {
	prefix    string
	data      dataType
	children  [224]*tree
	parameter *tree
	wildcard  *tree
	kind      byte
}

// add adds a new element to the tree.
func (node *tree) add(path string, data dataType) {
	// Search tree for equal parts until we can no longer proceed
	i := 0
	offset := 0

	for {
	begin:
		switch node.kind {
		case parameter:
			// This only occurs when the same parameter based route is added twice.
			// node: /post/:id|
			// path: /post/:id|
			if i == len(path) {
				node.data = data
				return
			}

			// When we hit a separator, we'll search for a fitting child.
			if path[i] == separator {
				var control controlFlow
				node, offset, control = node.end(path, data, i, offset)

				switch control {
				case controlStop:
					return
				case controlBegin:
					goto begin
				case controlNext:
					goto next
				}
			}

		default:
			if i == len(path) {
				// The path already exists.
				// node: /blog|
				// path: /blog|
				if i-offset == len(node.prefix) {
					node.data = data
					return
				}

				// The path ended but the node prefix is longer.
				// node: /blog|feed
				// path: /blog|
				node.split(i-offset, "", data)
				return
			}

			// The node we just checked is entirely included in our path.
			// node: /|
			// path: /|blog
			if i-offset == len(node.prefix) {
				var control controlFlow
				node, offset, control = node.end(path, data, i, offset)

				switch control {
				case controlStop:
					return
				case controlBegin:
					goto begin
				case controlNext:
					goto next
				}
			}

			// We got a conflict.
			// node: /b|ag
			// path: /b|riefcase
			if path[i] != node.prefix[i-offset] {
				node.split(i-offset, path[i:], data)
				return
			}
		}

	next:
		i++
	}
}

// split splits the node at the given index and inserts
// a new child node with the given path and data.
// If path is empty, it will not create another child node
// and instead assign the data directly to the node.
func (node *tree) split(index int, path string, data dataType) {
	// Create split node with the remaining string
	splitNode := node.clone(node.prefix[index:])

	/// The existing data must be removed
	node.reset(node.prefix[:index])

	// If the path is empty, it means we don't create a 2nd child node.
	// Just assign the data for the existing node and store a single child node.
	if path == "" {
		node.data = data
		node.children[splitNode.prefix[0]-32] = splitNode
		return
	}

	node.children[splitNode.prefix[0]-32] = splitNode

	// Create new nodes with the remaining path
	node.append(path, data)
}

// clone clones the node with a new prefix.
func (node *tree) clone(prefix string) *tree {
	return &tree{
		prefix:    prefix,
		data:      node.data,
		children:  node.children,
		parameter: node.parameter,
		wildcard:  node.wildcard,
		kind:      node.kind,
	}
}

// reset resets the existing node data.
func (node *tree) reset(prefix string) {
	node.prefix = prefix
	node.data = nil
	node.parameter = nil
	node.wildcard = nil
	node.kind = 0
	node.children = [224]*tree{}
}

// addTrailingSlash adds a trailing slash with the same data.
func (node *tree) addTrailingSlash(data dataType) {
	if strings.HasSuffix(node.prefix, "/") || node.children[separator-32] != nil || node.kind == wildcard {
		return
	}

	node.children[separator-32] = &tree{
		prefix: "/",
		data:   data,
	}
}

// append appends the given path to the tree.
func (node *tree) append(path string, data dataType) {
	// At this point, all we know is that somewhere
	// in the remaining string we have parameters.
	// node: /user|
	// path: /user|/:userid
	for {
		if path == "" {
			node.data = data
			return
		}

		paramStart := strings.IndexByte(path, parameter)

		if paramStart == -1 {
			paramStart = strings.IndexByte(path, wildcard)
		}

		// If it's a static route we are adding,
		// just add the remainder as a normal node.
		if paramStart == -1 {
			// If the node itself doesn't have a prefix (root node),
			// don't add a child and use the node itself.
			if node.prefix == "" {
				node.prefix = path
				node.data = data
				return
			}

			child := &tree{
				prefix: path,
				data:   data,
			}

			node.children[path[0]-32] = child
			child.addTrailingSlash(data)
			return
		}

		// If we're directly in front of a parameter,
		// add a parameter node.
		if paramStart == 0 {
			paramEnd := strings.IndexByte(path, separator)

			if paramEnd == -1 {
				paramEnd = len(path)
			}

			child := &tree{
				prefix: path[1:paramEnd],
				kind:   path[paramStart],
			}

			switch child.kind {
			case parameter:
				child.addTrailingSlash(data)
				node.parameter = child
				node = child
				path = path[paramEnd:]
				continue

			case wildcard:
				child.data = data
				node.wildcard = child
				return
			}
		}

		// We know there's a parameter, but not directly at the start.

		// If the node itself doesn't have a prefix (root node),
		// don't add a child and use the node itself.
		if node.prefix == "" {
			node.prefix = path[:paramStart]
			path = path[paramStart:]
			continue
		}

		// Add a normal node with the path before the parameter start.
		child := &tree{
			prefix: path[:paramStart],
		}

		// Allow trailing slashes to return
		// the same content as their parent node.
		if child.prefix == "/" {
			child.data = node.data
		}

		node.children[path[0]-32] = child
		node = child
		path = path[paramStart:]
	}
}

// end is called when the node was fully parsed
// and needs to decide the next control flow.
func (node *tree) end(path string, data dataType, i int, offset int) (*tree, int, controlFlow) {
	child := node.children[path[i]-32]

	if child != nil {
		node = child
		offset = i
		return node, offset, controlNext
	}

	// No fitting children found, does this node even contain a prefix yet?
	// If no prefix is set, this is the starting node.
	if node.prefix == "" {
		node.append(path[i:], data)
		return node, offset, controlStop
	}

	// node: /user/|:id
	// path: /user/|:id/profile
	if node.parameter != nil {
		node = node.parameter
		offset = i
		return node, offset, controlBegin
	}

	node.append(path[i:], data)
	return node, offset, controlStop
}

// find finds the data for the given path and assigns it to ctx.handler, if available.
func (node *tree) find(path string, ctx *context) {
	var (
		i                  uint
		offset             uint
		lastWildcardOffset uint
		lastWildcard       *tree
	)

	// Search tree for equal parts until we can no longer proceed
	for {
	begin:
		switch node.kind {
		case parameter:
			if i == uint(len(path)) {
				ctx.addParameter(node.prefix, path[offset:i])
				ctx.handler = node.data
				return
			}

			if path[i] == separator {
				ctx.addParameter(node.prefix, path[offset:i])
				node = node.children[separator-32]
				offset = i
				i++
				goto begin
			}

		default:
			// We reached the end.
			if i == uint(len(path)) {
				// node: /blog|
				// path: /blog|
				if i-offset == uint(len(node.prefix)) {
					ctx.handler = node.data
					return
				}

				// node: /blog|feed
				// path: /blog|
				ctx.handler = nil
				return
			}

			// The node we just checked is entirely included in our path.
			// node: /|
			// path: /|blog
			if i-offset == uint(len(node.prefix)) {
				if node.wildcard != nil {
					lastWildcard = node.wildcard
					lastWildcardOffset = i
				}

				child := node.children[path[i]-32]

				if child != nil {
					node = child
					offset = i
					i++
					goto begin
				}

				// node: /|:id
				// path: /|blog
				if node.parameter != nil {
					node = node.parameter
					offset = i
					goto begin
				}

				// node: /|*any
				// path: /|image.png
				if node.wildcard != nil {
					ctx.addParameter(node.wildcard.prefix, path[i:])
					ctx.handler = node.wildcard.data
					return
				}

				ctx.handler = nil
				return
			}

			// We got a conflict.
			// node: /b|ag
			// path: /b|riefcase
			if path[i] != node.prefix[i-offset] {
				if lastWildcard != nil {
					ctx.addParameter(lastWildcard.prefix, path[lastWildcardOffset:])
					ctx.handler = lastWildcard.data
					return
				}

				ctx.handler = nil
				return
			}
		}

		i++
	}
}

// each traverses the tree and calls the given function on every node.
func (node *tree) each(callback func(*tree)) {
	callback(node)

	for _, child := range node.children {
		if child == nil {
			continue
		}

		child.each(callback)
	}

	if node.parameter != nil {
		node.parameter.each(callback)
	}

	if node.wildcard != nil {
		node.wildcard.each(callback)
	}
}

// PrettyPrint prints a human-readable form of the tree to the given writer.
func (node *tree) PrettyPrint(writer io.Writer) {
	node.prettyPrint(writer, -1)
}

// prettyPrint
func (node *tree) prettyPrint(writer io.Writer, level int) {
	prefix := ""

	if level >= 0 {
		prefix = strings.Repeat("  ", level) + "|_ "
	}

	colorFunc := color.CyanString

	switch node.kind {
	case ':':
		prefix += ":"
		colorFunc = color.YellowString
	case '*':
		prefix += "*"
		colorFunc = color.GreenString
	}

	fmt.Fprintf(writer, "%s%s [%t]\n", prefix, colorFunc(node.prefix), node.data != nil)

	for _, child := range node.children {
		if child == nil {
			continue
		}

		child.prettyPrint(writer, level+1)
	}

	if node.parameter != nil {
		node.parameter.prettyPrint(writer, level+1)
	}

	if node.wildcard != nil {
		node.wildcard.prettyPrint(writer, level+1)
	}
}
<|endoftext|>


<commit_before>package relations

import (
	"github.com/oleiade/lane"
)

type pair struct {
	in       string
	out      string
}

func (p *pair) contain(in, out string) bool {
	return p.in == in && p.out == out
}

func (p *pair) equal(o *pair) bool {
	return p.contain(o.in, o.out)
}

type node struct {
	operator string
	left     *node
	right    *node
	index    int
	nullable bool
	first    *set
	last     *set
}

// Computes nullable, firstPos and lastPos
func (n *node) annotate() {
	switch n.operator {
	case "*":
		n.nullable = true
		n.first = n.left.first.clone()
		n.last = n.left.last.clone()
	case "+":
		n.nullable = n.right.nullable || n.left.nullable
		n.first = n.left.first.union(n.right.first)
		n.last = n.left.last.union(n.right.last)
	case ".":
		n.nullable = n.right.nullable && n.left.nullable
		if n.left.nullable {
			n.first = n.left.first.union(n.right.first)
		} else {
			n.first = n.left.first.clone()
		}
		if n.right.nullable {
			n.last = n.left.last.union(n.right.last)
		} else {
			n.last = n.right.last.clone()
		}
	}
}

type tree struct {
	alphabet  []*pair
	rootFirst *set
	follow    map[int]*set
	symbols   map[int]*pair
}

func newTree() *tree {
	return &tree{follow: make(map[int]*set), symbols: make(map[int]*pair)}
}

func (t *tree) updateAlphabet(in, out string) *pair {
	for _, p := range t.alphabet {
		if p.contain(in, out) {
			return p
		}
	}
	newPair := &pair{in: in, out: out}
	// TODO: ???
	if in != "!" {
		t.alphabet = append(t.alphabet, newPair)
	}
	return newPair
}

func (t *tree) newLeafNode(in, out string, index int) *node {
	t.symbols[index] = t.updateAlphabet(in, out)
	newNode := &node{index: index, first: newSet(index), last: newSet(index)}
	if in == "" && out == "" {
		newNode.nullable = true
	}
	return newNode
}

func (t *tree) newOperatorNode(operator string, left, right *node) *node {
	newNode := &node{operator: operator, left: left, right: right}
	newNode.annotate()
	t.updateFollow(newNode)
	return newNode
}

// Updates followPos with information from newly created node
func (t *tree) updateFollow(n *node) {
	switch n.operator {
	case ".":
		for position := range *n.left.last {
			t.follow[position] = n.right.first.union(t.follow[position])
		}
	case "*":
		for position := range *n.last {
			t.follow[position] = n.first.union(t.follow[position])
		}
	}
}

// Builds parse tree from regular expression while computing
// nullable, firstPos, lastPos and followPos
func buildTree(raw string) *tree {
	t := newTree()

	nodeStack := lane.NewStack()
	operatorStack := lane.NewStack()

	position := 0
	index := 1

	for position < len(raw) {
		char := string(raw[position])
		switch char {
		case "<":
			var s, in, out string
			for {
				position += 1
				char = string(raw[position])
				if char == "," {
					in = s
					s = ""
					continue
				}
				if char == ">" {
					out = s
					break
				}
				s += char
			}

			nodeStack.Push(t.newLeafNode(in, out, index))
			index += 1

		case "(", "+", ".":
			operatorStack.Push(char)

		case ")":
			operator := operatorStack.Pop().(string)
			for operator != "(" {
				right := nodeStack.Pop().(*node)
				left := nodeStack.Pop().(*node)
				nodeStack.Push(t.newOperatorNode(operator, left, right))
				operator = operatorStack.Pop().(string)
			}

		case "*":
			operand := nodeStack.Pop().(*node)
			nodeStack.Push(t.newOperatorNode(char, operand, nil))
		}
		position += 1
	}

	// Add endmarker character
	operatorStack.Push(".")
	nodeStack.Push(t.newLeafNode("!", "", index))

	for !operatorStack.Empty() {
		operator := operatorStack.Pop().(string)
		right := nodeStack.Pop().(*node)
		left := nodeStack.Pop().(*node)
		nodeStack.Push(t.newOperatorNode(operator, left, right))
	}

	t.rootFirst = nodeStack.Pop().(*node).first
	return t
}
<commit_msg>Tweak construction of parse tree<commit_after>package relations

import (
	"github.com/oleiade/lane"
)

type pair struct {
	in  string
	out string
}

func (p *pair) contain(in, out string) bool {
	return p.in == in && p.out == out
}

func (p *pair) equal(o *pair) bool {
	return p.contain(o.in, o.out)
}

type node struct {
	operator string
	left     *node
	right    *node
	index    int
	nullable bool
	first    *set
	last     *set
}

// Computes nullable, firstPos and lastPos
func (n *node) annotate() {
	switch n.operator {
	case "*":
		n.nullable = true
		n.first = n.left.first.clone()
		n.last = n.left.last.clone()
	case "+":
		n.nullable = n.right.nullable || n.left.nullable
		n.first = n.left.first.union(n.right.first)
		n.last = n.left.last.union(n.right.last)
	case ".":
		n.nullable = n.right.nullable && n.left.nullable
		if n.left.nullable {
			n.first = n.left.first.union(n.right.first)
		} else {
			n.first = n.left.first.clone()
		}
		if n.right.nullable {
			n.last = n.left.last.union(n.right.last)
		} else {
			n.last = n.right.last.clone()
		}
	}
}

type tree struct {
	alphabet  []*pair
	rootFirst *set
	follow    map[int]*set
	symbols   map[int]*pair
}

func newTree() *tree {
	return &tree{follow: make(map[int]*set), symbols: make(map[int]*pair)}
}

func (t *tree) updateAlphabet(in, out string) *pair {
	for _, p := range t.alphabet {
		if p.contain(in, out) {
			return p
		}
	}
	newPair := &pair{in: in, out: out}
	// TODO: ???
	if in != "!" {
		t.alphabet = append(t.alphabet, newPair)
	}
	return newPair
}

func (t *tree) newLeafNode(in, out string, index int) *node {
	t.symbols[index] = t.updateAlphabet(in, out)
	newNode := &node{index: index, first: newSet(index), last: newSet(index)}
	if in == "" && out == "" {
		newNode.nullable = true
	}
	return newNode
}

func (t *tree) newOperatorNode(operator string, left, right *node) *node {
	newNode := &node{operator: operator, left: left, right: right}
	newNode.annotate()
	t.updateFollow(newNode)
	return newNode
}

// Updates followPos with information from newly created node
func (t *tree) updateFollow(n *node) {
	switch n.operator {
	case "*":
		for position := range *n.last {
			t.follow[position] = n.first.union(t.follow[position])
		}
	case ".":
		for position := range *n.left.last {
			t.follow[position] = n.right.first.union(t.follow[position])
		}
	}
}

// Builds parse tree from regular expression while computing
// nullable, firstPos, lastPos and followPos
func buildTree(raw string) *tree {
	t := newTree()

	nodeStack := lane.NewStack()
	operatorStack := lane.NewStack()

	position := 0
	index := 1

	for position < len(raw) {
		char := string(raw[position])
		switch char {
		case "<":
			var s, in, out string
			for {
				position += 1
				char = string(raw[position])
				if char == "," {
					in = s
					s = ""
					continue
				}
				if char == ">" {
					out = s
					break
				}
				s += char
			}

			nodeStack.Push(t.newLeafNode(in, out, index))
			index += 1

		case "(", "+", ".":
			operatorStack.Push(char)

		case ")":
			operator := operatorStack.Pop().(string)
			for operator != "(" {
				right := nodeStack.Pop().(*node)
				left := nodeStack.Pop().(*node)
				nodeStack.Push(t.newOperatorNode(operator, left, right))
				operator = operatorStack.Pop().(string)
			}

		case "*":
			operand := nodeStack.Pop().(*node)
			nodeStack.Push(t.newOperatorNode(char, operand, nil))
		}
		position += 1
	}

	for !operatorStack.Empty() {
		operator := operatorStack.Pop().(string)
		right := nodeStack.Pop().(*node)
		left := nodeStack.Pop().(*node)
		nodeStack.Push(t.newOperatorNode(operator, left, right))
	}

	// Add endmarker character
	right := t.newLeafNode("!", "", index)
	left := nodeStack.Pop().(*node)
	root := t.newOperatorNode(".", left, right)

	t.rootFirst = root.first
	return t
}
<|endoftext|>


<commit_before>/*Gospell suggests alternate spellings for words.

Based on https://github.com/AlanQuatermain/go-trie and
https://github.com/sbuss/pyspellsug
*/
package gospell

import (
	"fmt"
	"strings"
)

type children map[rune]*Trie

type Trie struct {
	children children
	leaf     bool
}

// Create a new Trie with no children and leaf=false
func NewTrie() *Trie {
	t := new(Trie)
	t.children = make(children)
	t.leaf = false
	return t
}

// Insert a strings.Reader into the Trie
func (t *Trie) Insert(s *strings.Reader) {
	rune, _, err := s.ReadRune()
	if err != nil {
		// We have reached EOF
		t.leaf = true
		return
	}

	child := t.children[rune]
	if child == nil {
		child = NewTrie()
		t.children[rune] = child
	}
	child.Insert(s)
}

// Insert a string into the Trie
func (t *Trie) InsertString(s string) {
	t.Insert(strings.NewReader(s))
}

// Get the Trie at the end of a strings.Reader
func (t *Trie) Get(s *strings.Reader) *Trie {
	rune, _, err := s.ReadRune()
	if err != nil {
		// We have reached EOF
		return t
	}
	t = t.children[rune]
	if t == nil {
		return nil
	}
	return t.Get(s)
}

// Return true if the Trie contains the word in a strings.Reader
// This should only be true if the word is a leaf. For example, say you
// Insert the strings Salmon and Salmonella. Sal will not be Contained in the
// Trie, but Salmon and Salmonella both are.
func (t *Trie) Contains(s *strings.Reader) bool {
	return t.Get(s).leaf
}

// Check is a String is Contained in the Trie. See Trie.Contains.
func (t *Trie) ContainsString(s string) bool {
	if s == "" {
		return false
	}

	return t.Contains(strings.NewReader(s))
}

// Get all of the complete child words under this Trie node
func (t *Trie) AllFullChildren() []string {
	childStrings := []string{}

	for r, child := range t.children {
		if child != nil {
			if child.leaf {
				childStrings = append(childStrings, string(r))
			}
			for _, ccs := range child.AllFullChildren() {
				childStrings = append(childStrings, string(r)+ccs)
			}
		}
	}
	return childStrings
}

// Convert a Trie to a String
func (t *Trie) String() string {
	c := ""
	for r, child := range t.children {
		c += fmt.Sprintf("%q: %v", r, child)
	}
	s := fmt.Sprintf("{leaf: %t, c: %v}", t.leaf, c)
	return s
}
<commit_msg>Don't try to access 'leaf' for a nil trie node<commit_after>/*Gospell suggests alternate spellings for words.

Based on https://github.com/AlanQuatermain/go-trie and
https://github.com/sbuss/pyspellsug
*/
package gospell

import (
	"fmt"
	"strings"
)

type children map[rune]*Trie

type Trie struct {
	children children
	leaf     bool
}

// Create a new Trie with no children and leaf=false
func NewTrie() *Trie {
	t := new(Trie)
	t.children = make(children)
	t.leaf = false
	return t
}

// Insert a strings.Reader into the Trie
func (t *Trie) Insert(s *strings.Reader) {
	rune, _, err := s.ReadRune()
	if err != nil {
		// We have reached EOF
		t.leaf = true
		return
	}

	child := t.children[rune]
	if child == nil {
		child = NewTrie()
		t.children[rune] = child
	}
	child.Insert(s)
}

// Insert a string into the Trie
func (t *Trie) InsertString(s string) {
	t.Insert(strings.NewReader(s))
}

// Get the Trie at the end of a strings.Reader
func (t *Trie) Get(s *strings.Reader) *Trie {
	rune, _, err := s.ReadRune()
	if err != nil {
		// We have reached EOF
		return t
	}
	t = t.children[rune]
	if t == nil {
		return nil
	}
	return t.Get(s)
}

// Return true if the Trie contains the word in a strings.Reader
// This should only be true if the word is a leaf. For example, say you
// Insert the strings Salmon and Salmonella. Sal will not be Contained in the
// Trie, but Salmon and Salmonella both are.
func (t *Trie) Contains(s *strings.Reader) bool {
	child := t.Get(s)
	if child != nil {
		return child.leaf
	}
	return false
}

// Check is a String is Contained in the Trie. See Trie.Contains.
func (t *Trie) ContainsString(s string) bool {
	if s == "" {
		return false
	}

	return t.Contains(strings.NewReader(s))
}

// Get all of the complete child words under this Trie node
func (t *Trie) AllFullChildren() []string {
	childStrings := []string{}

	for r, child := range t.children {
		if child != nil {
			if child.leaf {
				childStrings = append(childStrings, string(r))
			}
			for _, ccs := range child.AllFullChildren() {
				childStrings = append(childStrings, string(r)+ccs)
			}
		}
	}
	return childStrings
}

// Convert a Trie to a String
func (t *Trie) String() string {
	c := ""
	for r, child := range t.children {
		c += fmt.Sprintf("%q: %v", r, child)
	}
	s := fmt.Sprintf("{leaf: %t, c: %v}", t.leaf, c)
	return s
}
<|endoftext|>


<commit_before>// Copyright 2016 The Cockroach Authors.
//
// 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.
//
// Author: Tobias Schottdorf (tobias.schottdorf@gmail.com)

package acceptance

import (
	"bytes"
	gosql "database/sql"
	"fmt"
	"math/rand"
	"sort"
	"sync/atomic"
	"testing"
	"time"

	"golang.org/x/net/context"

	"github.com/cockroachdb/cockroach-go/crdb"
	"github.com/cockroachdb/cockroach/pkg/acceptance/cluster"
	"github.com/cockroachdb/cockroach/pkg/util/log"
)

// TestMonotonicInserts replicates the 'monotonic' test from the Jepsen
// CockroachDB test suite (https://github.com/cockroachdb/jepsen).
func TestMonotonicInserts(t *testing.T) {
	s := log.Scope(t, "")
	defer s.Close(t)

	runTestOnConfigs(t, testMonotonicInsertsInner)
}

type mtRow struct {
	val      int64
	sts      string
	node, tb int64
}

type mtRows []mtRow

func (r mtRows) Len() int {
	return len(r)
}

func (r mtRows) Less(i, j int) bool {
	if r[i].val == r[j].val {
		return r[i].sts < r[j].sts
	}
	return r[i].val < r[j].val
}

func (r mtRows) Swap(i, j int) {
	r[i], r[j] = r[j], r[i]
}

func (r mtRows) String() string {
	var buf bytes.Buffer
	for i, row := range r {
		prefix := "ok"
		if i > 0 && r.Less(i, i-1) {
			prefix = "!!"
		}
		fmt.Fprintf(&buf, "%s %+v\n", prefix, row)
	}
	return buf.String()
}

type mtClient struct {
	*gosql.DB
	ID int
}

func testMonotonicInsertsInner(
	ctx context.Context, t *testing.T, c cluster.Cluster, cfg cluster.TestConfig,
) {
	var clients []mtClient
	for i := 0; i < c.NumNodes(); i++ {
		clients = append(clients, mtClient{ID: i, DB: makePGClient(t, c.PGUrl(ctx, i))})
	}
	// We will insert into this table by selecting MAX(val) and increasing by
	// one and expect that val and sts (the commit timestamp) are both
	// simultaneously increasing.
	if _, err := clients[0].Exec(`
CREATE DATABASE mono;
CREATE TABLE IF NOT EXISTS mono.mono (val INT, sts STRING, node INT, tb INT);
INSERT INTO mono.mono VALUES(-1, '0', -1, -1)`); err != nil {
		t.Fatal(err)
	}

	var idGen uint64

	invoke := func(client mtClient) {
		logPrefix := fmt.Sprintf("%03d.%03d: ", atomic.AddUint64(&idGen, 1), client.ID)
		l := func(msg string, args ...interface{}) {
			log.Infof(ctx, logPrefix+msg, args...)
			if log.V(2) {
				t.Logf(logPrefix+msg, args...)
			}
		}
		l("begin")
		defer l("done")

		var exRow, insRow mtRow
		var attempt int
		if err := crdb.ExecuteTx(client.DB, func(tx *gosql.Tx) error {
			attempt++
			l("attempt %d", attempt)
			if err := tx.QueryRow(`SELECT cluster_logical_timestamp()`).Scan(
				&insRow.sts,
			); err != nil {
				l(err.Error())
				return err
			}

			l("read max val")
			if err := tx.QueryRow(`SELECT MAX(val) AS m FROM mono.mono`).Scan(
				&exRow.val,
			); err != nil {
				l(err.Error())
				return err
			}

			l("read max row for val=%d", exRow.val)
			if err := tx.QueryRow(`SELECT sts, node, tb FROM mono.mono WHERE val = $1`,
				exRow.val,
			).Scan(
				&exRow.sts, &exRow.node, &exRow.tb,
			); err != nil {
				l(err.Error())
				return err
			}

			l("insert")
			if err := tx.QueryRow(`
INSERT INTO mono.mono (val, sts, node, tb) VALUES($1, $2, $3, $4)
RETURNING val, sts, node, tb`,
				exRow.val+1, insRow.sts, client.ID, 0,
			).Scan(
				&insRow.val, &insRow.sts, &insRow.node, &insRow.tb,
			); err != nil {
				l(err.Error())
				return err
			}
			l("commit")
			return nil
		}); err != nil {
			t.Errorf("%T: %v", err, err)
		}
	}

	verify := func() {
		client := clients[0]
		var numDistinct int
		if err := client.QueryRow("SELECT COUNT(DISTINCT(val)) FROM mono.mono").Scan(
			&numDistinct,
		); err != nil {
			t.Fatal(err)
		}
		rows, err := client.Query("SELECT val, sts, node, tb FROM mono.mono ORDER BY val ASC, sts ASC")
		if err != nil {
			t.Fatal(err)
		}
		var results mtRows
		for rows.Next() {
			var row mtRow
			if err := rows.Scan(&row.val, &row.sts, &row.node, &row.tb); err != nil {
				t.Fatal(err)
			}
			results = append(results, row)
		}

		if !sort.IsSorted(results) {
			t.Errorf("results are not sorted:\n%s", results)
		}

		if numDistinct != len(results) {
			t.Errorf("'val' column is not unique: %d results, but %d distinct:\n%s",
				len(results), numDistinct, results)
		}
	}

	concurrency := 2 * c.NumNodes()

	sem := make(chan struct{}, concurrency)
	timer := time.After(cfg.Duration)

	defer verify()
	defer func() {
		// Now that consuming has stopped, fill up the semaphore (i.e. wait for
		// still-running goroutines to stop)
		for i := 0; i < concurrency; i++ {
			sem <- struct{}{}
		}
	}()

	for {
		select {
		case sem <- struct{}{}:
		case <-stopper.ShouldStop():
			return
		case <-timer:
			return
		}
		go func(client mtClient) {
			invoke(client)
			<-sem
		}(clients[rand.Intn(c.NumNodes())])
	}
}
<commit_msg>acceptance: skip TestMonotonicInserts<commit_after>// Copyright 2016 The Cockroach Authors.
//
// 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.
//
// Author: Tobias Schottdorf (tobias.schottdorf@gmail.com)

package acceptance

import (
	"bytes"
	gosql "database/sql"
	"fmt"
	"math/rand"
	"sort"
	"sync/atomic"
	"testing"
	"time"

	"golang.org/x/net/context"

	"github.com/cockroachdb/cockroach-go/crdb"
	"github.com/cockroachdb/cockroach/pkg/acceptance/cluster"
	"github.com/cockroachdb/cockroach/pkg/util/log"
)

// TestMonotonicInserts replicates the 'monotonic' test from the Jepsen
// CockroachDB test suite (https://github.com/cockroachdb/jepsen).
func TestMonotonicInserts(t *testing.T) {
	t.Skip("#13759")
	s := log.Scope(t, "")
	defer s.Close(t)

	runTestOnConfigs(t, testMonotonicInsertsInner)
}

type mtRow struct {
	val      int64
	sts      string
	node, tb int64
}

type mtRows []mtRow

func (r mtRows) Len() int {
	return len(r)
}

func (r mtRows) Less(i, j int) bool {
	if r[i].val == r[j].val {
		return r[i].sts < r[j].sts
	}
	return r[i].val < r[j].val
}

func (r mtRows) Swap(i, j int) {
	r[i], r[j] = r[j], r[i]
}

func (r mtRows) String() string {
	var buf bytes.Buffer
	for i, row := range r {
		prefix := "ok"
		if i > 0 && r.Less(i, i-1) {
			prefix = "!!"
		}
		fmt.Fprintf(&buf, "%s %+v\n", prefix, row)
	}
	return buf.String()
}

type mtClient struct {
	*gosql.DB
	ID int
}

func testMonotonicInsertsInner(
	ctx context.Context, t *testing.T, c cluster.Cluster, cfg cluster.TestConfig,
) {
	var clients []mtClient
	for i := 0; i < c.NumNodes(); i++ {
		clients = append(clients, mtClient{ID: i, DB: makePGClient(t, c.PGUrl(ctx, i))})
	}
	// We will insert into this table by selecting MAX(val) and increasing by
	// one and expect that val and sts (the commit timestamp) are both
	// simultaneously increasing.
	if _, err := clients[0].Exec(`
CREATE DATABASE mono;
CREATE TABLE IF NOT EXISTS mono.mono (val INT, sts STRING, node INT, tb INT);
INSERT INTO mono.mono VALUES(-1, '0', -1, -1)`); err != nil {
		t.Fatal(err)
	}

	var idGen uint64

	invoke := func(client mtClient) {
		logPrefix := fmt.Sprintf("%03d.%03d: ", atomic.AddUint64(&idGen, 1), client.ID)
		l := func(msg string, args ...interface{}) {
			log.Infof(ctx, logPrefix+msg, args...)
			if log.V(2) {
				t.Logf(logPrefix+msg, args...)
			}
		}
		l("begin")
		defer l("done")

		var exRow, insRow mtRow
		var attempt int
		if err := crdb.ExecuteTx(client.DB, func(tx *gosql.Tx) error {
			attempt++
			l("attempt %d", attempt)
			if err := tx.QueryRow(`SELECT cluster_logical_timestamp()`).Scan(
				&insRow.sts,
			); err != nil {
				l(err.Error())
				return err
			}

			l("read max val")
			if err := tx.QueryRow(`SELECT MAX(val) AS m FROM mono.mono`).Scan(
				&exRow.val,
			); err != nil {
				l(err.Error())
				return err
			}

			l("read max row for val=%d", exRow.val)
			if err := tx.QueryRow(`SELECT sts, node, tb FROM mono.mono WHERE val = $1`,
				exRow.val,
			).Scan(
				&exRow.sts, &exRow.node, &exRow.tb,
			); err != nil {
				l(err.Error())
				return err
			}

			l("insert")
			if err := tx.QueryRow(`
INSERT INTO mono.mono (val, sts, node, tb) VALUES($1, $2, $3, $4)
RETURNING val, sts, node, tb`,
				exRow.val+1, insRow.sts, client.ID, 0,
			).Scan(
				&insRow.val, &insRow.sts, &insRow.node, &insRow.tb,
			); err != nil {
				l(err.Error())
				return err
			}
			l("commit")
			return nil
		}); err != nil {
			t.Errorf("%T: %v", err, err)
		}
	}

	verify := func() {
		client := clients[0]
		var numDistinct int
		if err := client.QueryRow("SELECT COUNT(DISTINCT(val)) FROM mono.mono").Scan(
			&numDistinct,
		); err != nil {
			t.Fatal(err)
		}
		rows, err := client.Query("SELECT val, sts, node, tb FROM mono.mono ORDER BY val ASC, sts ASC")
		if err != nil {
			t.Fatal(err)
		}
		var results mtRows
		for rows.Next() {
			var row mtRow
			if err := rows.Scan(&row.val, &row.sts, &row.node, &row.tb); err != nil {
				t.Fatal(err)
			}
			results = append(results, row)
		}

		if !sort.IsSorted(results) {
			t.Errorf("results are not sorted:\n%s", results)
		}

		if numDistinct != len(results) {
			t.Errorf("'val' column is not unique: %d results, but %d distinct:\n%s",
				len(results), numDistinct, results)
		}
	}

	concurrency := 2 * c.NumNodes()

	sem := make(chan struct{}, concurrency)
	timer := time.After(cfg.Duration)

	defer verify()
	defer func() {
		// Now that consuming has stopped, fill up the semaphore (i.e. wait for
		// still-running goroutines to stop)
		for i := 0; i < concurrency; i++ {
			sem <- struct{}{}
		}
	}()

	for {
		select {
		case sem <- struct{}{}:
		case <-stopper.ShouldStop():
			return
		case <-timer:
			return
		}
		go func(client mtClient) {
			invoke(client)
			<-sem
		}(clients[rand.Intn(c.NumNodes())])
	}
}
<|endoftext|>


<commit_before>package service

import (
	"fmt"
	"net"
	"strconv"

	"github.com/rancher/norman/httperror"
	"github.com/rancher/norman/types"
	"github.com/rancher/norman/types/convert"
	v3 "github.com/rancher/rancher/pkg/client/generated/project/v3"
	"github.com/sirupsen/logrus"
	"k8s.io/apimachinery/pkg/util/intstr"
)

func New(store types.Store) types.Store {
	return &Store{
		store,
	}
}

type Store struct {
	types.Store
}

func (p *Store) Create(apiContext *types.APIContext, schema *types.Schema, data map[string]interface{}) (map[string]interface{}, error) {
	if schema.ID == "dnsRecord" {
		if convert.IsAPIObjectEmpty(data["hostname"]) {
			data["kind"] = "ClusterIP"
			data["clusterIp"] = nil
		} else {
			data["kind"] = "ExternalName"
			data["clusterIp"] = ""
		}
	}
	formatData(schema, data)
	err := p.validateNonSpecialIP(schema, data)
	if err != nil {
		return nil, err
	}
	return p.Store.Create(apiContext, schema, data)
}

func formatData(schema *types.Schema, data map[string]interface{}) {
	var ports []interface{}
	if schema.ID == "service" {
		ports = convert.ToInterfaceSlice(data["ports"])
	}
	// append default port as sky dns won't work w/o at least one port being set
	if len(ports) == 0 {
		servicePort := v3.ServicePort{
			Port:       42,
			TargetPort: intstr.Parse(strconv.FormatInt(42, 10)),
			Protocol:   "TCP",
			Name:       "default",
		}
		m, err := convert.EncodeToMap(servicePort)
		if err != nil {
			logrus.Warnf("Failed to transform service port to map: %v", err)
			return
		}
		ports = append(ports, m)
	}
	data["ports"] = ports
}

func (p *Store) validateNonSpecialIP(schema *types.Schema, data map[string]interface{}) error {
	if schema.ID == "dnsRecord" {
		ips := data["ipAddresses"]
		if ips != nil {
			for _, ip := range ips.([]interface{}) {
				IP := net.ParseIP(ip.(string))
				if IP == nil {
					return httperror.NewAPIError(httperror.InvalidOption, fmt.Sprintf("%s must be a valid IP address", IP))
				}
				if IP.IsUnspecified() {
					return httperror.NewAPIError(httperror.InvalidOption, fmt.Sprintf("%s may not be unspecified (0.0.0.0)", IP))
				}
				if IP.IsLoopback() {
					return httperror.NewAPIError(httperror.InvalidOption, fmt.Sprintf("%s may not be in the loopback range (127.0.0.0/8)", IP))
				}
				if IP.IsLinkLocalUnicast() {
					return httperror.NewAPIError(httperror.InvalidOption, fmt.Sprintf("%s may not be in the link-local range (169.254.0.0/16)", IP))
				}
				if IP.IsLinkLocalMulticast() {
					return httperror.NewAPIError(httperror.InvalidOption, fmt.Sprintf("%s may not be in the link-local multicast range (224.0.0.0/24)", IP))
				}
			}
		}
	}
	return nil
}
<commit_msg>Default to SingleStack for headless services<commit_after>package service

import (
	"fmt"
	"net"
	"strconv"

	"github.com/rancher/norman/httperror"
	"github.com/rancher/norman/types"
	"github.com/rancher/norman/types/convert"
	v3 "github.com/rancher/rancher/pkg/client/generated/project/v3"
	"github.com/sirupsen/logrus"
	"k8s.io/apimachinery/pkg/util/intstr"
)

func New(store types.Store) types.Store {
	return &Store{
		store,
	}
}

type Store struct {
	types.Store
}

func (p *Store) Create(apiContext *types.APIContext, schema *types.Schema, data map[string]interface{}) (map[string]interface{}, error) {
	if schema.ID == "dnsRecord" {
		if convert.IsAPIObjectEmpty(data["hostname"]) {
			data["kind"] = "ClusterIP"
			data["clusterIp"] = nil
		} else {
			data["kind"] = "ExternalName"
			data["clusterIp"] = ""
		}
	}
	if schema.ID == "service" {
		logrus.Tracef("Service: Create: data.kind [%v], data.clusterIp [%v]", data["kind"], data["clusterIp"])
		if data["kind"] == "ClusterIP" && data["clusterIp"] == nil {
			if data["ipFamilyPolicy"] == nil {
				logrus.Debugf("Setting ipFamilyPolicy to SingleStack for service name [%s] service kind [%s]", data["name"], data["kind"])
				data["ipFamilyPolicy"] = "SingleStack"
			}
		}
	}
	formatData(schema, data)
	err := p.validateNonSpecialIP(schema, data)
	if err != nil {
		return nil, err
	}
	return p.Store.Create(apiContext, schema, data)
}

func formatData(schema *types.Schema, data map[string]interface{}) {
	var ports []interface{}
	if schema.ID == "service" {
		ports = convert.ToInterfaceSlice(data["ports"])
	}
	// append default port as sky dns won't work w/o at least one port being set
	if len(ports) == 0 {
		servicePort := v3.ServicePort{
			Port:       42,
			TargetPort: intstr.Parse(strconv.FormatInt(42, 10)),
			Protocol:   "TCP",
			Name:       "default",
		}
		m, err := convert.EncodeToMap(servicePort)
		if err != nil {
			logrus.Warnf("Failed to transform service port to map: %v", err)
			return
		}
		ports = append(ports, m)
	}
	data["ports"] = ports
}

func (p *Store) validateNonSpecialIP(schema *types.Schema, data map[string]interface{}) error {
	if schema.ID == "dnsRecord" {
		ips := data["ipAddresses"]
		if ips != nil {
			for _, ip := range ips.([]interface{}) {
				IP := net.ParseIP(ip.(string))
				if IP == nil {
					return httperror.NewAPIError(httperror.InvalidOption, fmt.Sprintf("%s must be a valid IP address", IP))
				}
				if IP.IsUnspecified() {
					return httperror.NewAPIError(httperror.InvalidOption, fmt.Sprintf("%s may not be unspecified (0.0.0.0)", IP))
				}
				if IP.IsLoopback() {
					return httperror.NewAPIError(httperror.InvalidOption, fmt.Sprintf("%s may not be in the loopback range (127.0.0.0/8)", IP))
				}
				if IP.IsLinkLocalUnicast() {
					return httperror.NewAPIError(httperror.InvalidOption, fmt.Sprintf("%s may not be in the link-local range (169.254.0.0/16)", IP))
				}
				if IP.IsLinkLocalMulticast() {
					return httperror.NewAPIError(httperror.InvalidOption, fmt.Sprintf("%s may not be in the link-local multicast range (224.0.0.0/24)", IP))
				}
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package fs

import (
	"fmt"
	"github.com/go-errors/errors"
	"github.com/go-ini/ini"
	"path/filepath"
	"strings"
)

type File struct {
	path     string
	engine   *ini.File
	sections map[string]*ini.Section
}

func LoadFile(path string) (*File, error) {
	f, err := ini.Load(path)

	if err != nil {
		return nil, errors.New(err)
	}

	allSections := f.Sections()
	sections := make(map[string]*ini.Section)

	for _, section := range allSections {
		if section.HasKey("Exec") {
			sections[section.Name()] = section
		}
	}

	return &File{
		path:     path,
		engine:   f,
		sections: sections,
	}, nil
}

func (f *File) Name() string {
	return strings.Replace(filepath.Base(f.path), filepath.Ext(f.path), "", 1)
}

func (f *File) GetValues() (map[string]string, error) {
	result := make(map[string]string)
	var err error

	for name, section := range f.sections {
		key, failure := f.getKey(section, "Exec")

		if failure != nil {
			err = failure
			break
		}

		result[name] = key.Value()
	}

	if err != nil {
		return nil, errors.New(err)
	}

	return result, nil
}

func (f *File) SetValues(values map[string]string) error {
	var err error

	for name, value := range values {
		key, failure := f.getKey(f.sections[name], "Exec")

		if failure != nil {
			err = failure
			break
		}

		if key == nil {
			continue
		}

		key.SetValue(value)
	}

	if err != nil {
		return errors.New(err)
	}

	return nil
}

func (f *File) Save() error {
	f.normalizeValues()

	if err := f.engine.SaveTo(f.path); err != nil {
		return errors.New(err)
	}

	return nil
}

func (f *File) normalizeValues() {
	for _, section := range f.sections {
		keys := section.Keys()

		for _, key := range keys {
			val := key.Value()

			if key.Comment != "" && strings.Contains(key.Comment, ";") {
				key.SetValue(fmt.Sprintf("%s%s", val, key.Comment))
				key.Comment = ""
			}
		}
	}
}

func (f *File) getKey(section *ini.Section, keyName string) (*ini.Key, error) {
	if section == nil {
		return nil, nil
	}

	key, err := section.GetKey(keyName)

	if err != nil {
		return nil, err
	}

	return key, nil
}
<commit_msg>Added quotes encoding<commit_after>package fs

import (
	"fmt"
	"github.com/go-errors/errors"
	"github.com/go-ini/ini"
	"path/filepath"
	"strings"
)

type File struct {
	path     string
	engine   *ini.File
	sections map[string]*ini.Section
}

func LoadFile(path string) (*File, error) {
	f, err := ini.Load(path)

	if err != nil {
		return nil, errors.New(err)
	}

	allSections := f.Sections()
	sections := make(map[string]*ini.Section)

	for _, section := range allSections {
		if section.HasKey("Exec") {
			sections[section.Name()] = section
		}
	}

	return &File{
		path:     path,
		engine:   f,
		sections: sections,
	}, nil
}

func (f *File) Name() string {
	return strings.Replace(filepath.Base(f.path), filepath.Ext(f.path), "", 1)
}

func (f *File) GetValues() (map[string]string, error) {
	result := make(map[string]string)
	var err error

	for name, section := range f.sections {
		key, failure := f.getKey(section, "Exec")

		if failure != nil {
			err = failure
			break
		}

		result[name] = key.Value()
	}

	if err != nil {
		return nil, errors.New(err)
	}

	return result, nil
}

func (f *File) SetValues(values map[string]string) error {
	var err error

	for name, value := range values {
		key, failure := f.getKey(f.sections[name], "Exec")

		if failure != nil {
			err = failure
			break
		}

		if key == nil {
			continue
		}

		key.SetValue(value)
	}

	if err != nil {
		return errors.New(err)
	}

	return nil
}

func (f *File) Save() error {
	f.normalizeValues()

	if err := f.engine.SaveTo(f.path); err != nil {
		return errors.New(err)
	}

	return nil
}

func (f *File) normalizeValues() {
	for _, section := range f.sections {
		keys := section.Keys()

		for _, key := range keys {
			val := key.Value()

			if key.Comment != "" && strings.Contains(key.Comment, ";") {
				key.SetValue(f.encode(fmt.Sprintf("%s%s", val, key.Comment)))
				key.Comment = ""
			}
		}
	}
}

func (f *File) getKey(section *ini.Section, keyName string) (*ini.Key, error) {
	if section == nil {
		return nil, nil
	}

	key, err := section.GetKey(keyName)

	if err != nil {
		return nil, err
	}

	return key, nil
}

func (f *File) encode(s string) string {
	return fmt.Sprintf("`%s`", s)
}
<|endoftext|>


<commit_before>package handlers

import (
	"github.com/gilcrest/go-API-template/pkg/env"
	"github.com/gorilla/mux"
)

// PathMatch is a way of organizing routing to handlers (versioning as well)
func PathMatch(env *env.Env, rtr *mux.Router) *mux.Router {

	// match only POST requests on /api/appUser/create
	// This is the original (v1) version for the API and the response for this will never change
	//  with versioning in order to maintain a stable contract
	// func (r *Router) HandleFunc(path string, f func(http.ResponseWriter, *http.Request)) *Route
	rtr.Handle("/api/appUser", Handler{env, CreateUserHandler}).
		Methods("POST").
		Headers("Content-Type", "application/json")

	// match only POST requests on /api/v1/appUser/create
	// func(path string, handler http.Handler) *Route
	rtr.Handle("/api/v1/appUser", Handler{env, CreateUserHandler}).
		Methods("POST").
		Headers("Content-Type", "application/json")

	return rtr
}
<commit_msg>Comment cleanup<commit_after>package handlers

import (
	"github.com/gilcrest/go-API-template/pkg/env"
	"github.com/gorilla/mux"
)

// PathMatch is a way of organizing routing to handlers (versioning as well)
func PathMatch(env *env.Env, rtr *mux.Router) *mux.Router {

	// match only POST requests on /api/appUser/create
	// This is the original (v1) version for the API and the response for this will never change
	//  with versioning in order to maintain a stable contract
	rtr.Handle("/api/appUser", Handler{env, CreateUserHandler}).
		Methods("POST").
		Headers("Content-Type", "application/json")

	// match only POST requests on /api/v1/appUser/create
	rtr.Handle("/api/v1/appUser", Handler{env, CreateUserHandler}).
		Methods("POST").
		Headers("Content-Type", "application/json")

	return rtr
}
<|endoftext|>


<commit_before>// Copyright 2016-2018 Authors of Cilium
//
// 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.
//
// +build linux

package route

import (
	"fmt"
	"net"
	"time"

	"github.com/cilium/cilium/pkg/datapath/linux/linux_defaults"
	"github.com/cilium/cilium/pkg/mtu"

	"github.com/vishvananda/netlink"
)

const (
	// RouteReplaceMaxTries is the number of attempts the route will be
	// attempted to be added or updated in case the kernel returns an error
	RouteReplaceMaxTries = 10

	// RouteReplaceRetryInterval is the interval in which
	// RouteReplaceMaxTries attempts are attempted
	RouteReplaceRetryInterval = 100 * time.Millisecond

	// RTN_LOCAL is a route type used to indicate packet should be "routed"
	// locally and passed up the stack. Is used by IPSec to force encrypted
	// packets to pass through XFRM layer.
	RTN_LOCAL = 0x2
)

// getNetlinkRoute returns the route configuration as netlink.Route
func (r *Route) getNetlinkRoute() netlink.Route {
	rt := netlink.Route{
		Dst:      &r.Prefix,
		Src:      r.Local,
		MTU:      r.MTU,
		Protocol: r.Proto,
		Table:    r.Table,
		Type:     r.Type,
	}

	if r.Nexthop != nil {
		rt.Gw = *r.Nexthop
	}

	if r.Scope != netlink.SCOPE_UNIVERSE {
		rt.Scope = r.Scope
	} else if r.Scope == netlink.SCOPE_UNIVERSE && r.Type == RTN_LOCAL {
		rt.Scope = netlink.SCOPE_HOST
	}

	return rt
}

// getNexthopAsIPNet returns the nexthop of the route as IPNet
func (r *Route) getNexthopAsIPNet() *net.IPNet {
	if r.Nexthop == nil {
		return nil
	}

	if r.Nexthop.To4() != nil {
		return &net.IPNet{IP: *r.Nexthop, Mask: net.CIDRMask(32, 32)}
	}

	return &net.IPNet{IP: *r.Nexthop, Mask: net.CIDRMask(128, 128)}
}

func ipFamily(ip net.IP) int {
	if ip.To4() == nil {
		return netlink.FAMILY_V6
	}

	return netlink.FAMILY_V4
}

// Lookup attempts to find the linux route based on the route specification.
// If the route exists, the route is returned, otherwise an error is returned.
func Lookup(route Route) (*Route, error) {
	link, err := netlink.LinkByName(route.Device)
	if err != nil {
		return nil, fmt.Errorf("unable to find interface '%s' of route: %s", route.Device, err)
	}

	routeSpec := route.getNetlinkRoute()

	nlRoute := lookup(link, &routeSpec)
	if nlRoute == nil {
		return nil, nil
	}

	result := &Route{
		Local:   nlRoute.Src,
		Device:  link.Attrs().Name,
		MTU:     nlRoute.MTU,
		Scope:   nlRoute.Scope,
		Nexthop: &nlRoute.Gw,
	}

	if nlRoute.Dst != nil {
		result.Prefix = *nlRoute.Dst
	}

	return result, nil
}

// lookup finds a particular route as specified by the filter which points
// to the specified device. The filter route can have the following fields set:
//  - Dst
//  - LinkIndex
//  - Scope
//  - Gw
func lookup(link netlink.Link, route *netlink.Route) *netlink.Route {
	routes, err := netlink.RouteList(link, ipFamily(route.Dst.IP))
	if err != nil {
		return nil
	}

	for _, r := range routes {
		if r.Dst != nil && route.Dst == nil {
			continue
		}

		if route.Dst != nil && r.Dst == nil {
			continue
		}

		if route.Table != 0 && route.Table != r.Table {
			continue
		}

		aMaskLen, aMaskBits := r.Dst.Mask.Size()
		bMaskLen, bMaskBits := route.Dst.Mask.Size()
		if r.Scope == route.Scope &&
			aMaskLen == bMaskLen && aMaskBits == bMaskBits &&
			r.Dst.IP.Equal(route.Dst.IP) && r.Gw.Equal(route.Gw) {
			return &r
		}
	}

	return nil
}

func createNexthopRoute(link netlink.Link, routerNet *net.IPNet) *netlink.Route {
	// This is the L2 route which makes router IP available behind the
	// interface.
	rt := &netlink.Route{
		LinkIndex: link.Attrs().Index,
		Dst:       routerNet,
	}

	// Known issue: scope for IPv6 routes is not propagated correctly. If
	// we set the scope here, lookup() will be unable to identify the route
	// again and we will continously re-add the route
	if routerNet.IP.To4() != nil {
		rt.Scope = netlink.SCOPE_LINK
	}

	return rt
}

// replaceNexthopRoute verifies that the L2 route for the router IP which is
// used as nexthop for all node routes is properly installed. If unavailable or
// incorrect, it will be replaced with the proper L2 route.
func replaceNexthopRoute(link netlink.Link, routerNet *net.IPNet) (bool, error) {
	route := createNexthopRoute(link, routerNet)
	if lookup(link, route) == nil {
		if err := netlink.RouteReplace(route); err != nil {
			return false, fmt.Errorf("unable to add L2 nexthop route: %s", err)
		}

		return true, nil
	}

	return false, nil
}

// deleteNexthopRoute deletes
func deleteNexthopRoute(link netlink.Link, routerNet *net.IPNet) error {
	route := createNexthopRoute(link, routerNet)
	if err := netlink.RouteDel(route); err != nil {
		return fmt.Errorf("unable to delete L2 nexthop route: %s", err)
	}

	return nil
}

// Upsert adds or updates a Linux kernel route. The route described can be in
// the following two forms:
//
// direct:
//   prefix dev foo
//
// nexthop:
//   prefix via nexthop dev foo
//
// If a nexthop route is specified, this function will check whether a direct
// route to the nexthop exists and add if required. This means that the
// following two routes will exist afterwards:
//
//   nexthop dev foo
//   prefix via nexthop dev foo
//
// Due to a bug in the Linux kernel, the prefix route is attempted to be
// updated RouteReplaceMaxTries with an interval of RouteReplaceRetryInterval.
// This is a workaround for a race condition in which the direct route to the
// nexthop is not available immediately and the prefix route can fail with
// EINVAL if the Netlink calls are issued in short order.
//
// An error is returned if the route can not be added or updated.
func Upsert(route Route, mtuConfig mtu.Configuration) (bool, error) {
	var nexthopRouteCreated bool

	link, err := netlink.LinkByName(route.Device)
	if err != nil {
		return false, fmt.Errorf("unable to lookup interface %s: %s", route.Device, err)
	}

	routerNet := route.getNexthopAsIPNet()
	if routerNet != nil {
		if _, err := replaceNexthopRoute(link, routerNet); err != nil {
			return false, fmt.Errorf("unable to add nexthop route: %s", err)
		}

		nexthopRouteCreated = true
	}

	routeSpec := route.getNetlinkRoute()
	routeSpec.LinkIndex = link.Attrs().Index

	if routeSpec.MTU != 0 {
		// If the route includes the local address, then the route is for
		// local containers and we can use a high MTU for transmit. Otherwise,
		// it needs to be able to fit within the MTU of tunnel devices.
		if route.Prefix.Contains(route.Local) {
			routeSpec.MTU = mtuConfig.GetDeviceMTU()
		} else {
			routeSpec.MTU = mtuConfig.GetRouteMTU()
		}
	}

	if lookup(link, &routeSpec) == nil {
		err := fmt.Errorf("routeReplace not called yet")

		// Workaround: See description of this function
		for i := 0; err != nil && i < RouteReplaceMaxTries; i++ {
			err = netlink.RouteReplace(&routeSpec)
			if err == nil {
				break
			}
			time.Sleep(RouteReplaceRetryInterval)
		}

		if err != nil {
			if nexthopRouteCreated {
				deleteNexthopRoute(link, routerNet)
			}
			return false, err
		}

		return true, nil
	}

	return false, nil
}

// Delete deletes a Linux route. An error is returned if the route does not
// exist or if the route could not be deleted.
func Delete(route Route) error {
	link, err := netlink.LinkByName(route.Device)
	if err != nil {
		return fmt.Errorf("unable to lookup interface %s: %s", route.Device, err)
	}

	// Deletion of routes with Nexthop or Local set fails for IPv6.
	// Therefore do not use getNetlinkRoute().
	routeSpec := netlink.Route{
		Dst:       &route.Prefix,
		LinkIndex: link.Attrs().Index,
	}

	// Scope can only be specified for IPv4
	if route.Prefix.IP.To4() != nil {
		routeSpec.Scope = route.Scope
	}

	if err := netlink.RouteDel(&routeSpec); err != nil {
		return err
	}

	return nil
}

func lookupRule(fwmark, table, family int) (bool, error) {
	rules, err := netlink.RuleList(family)
	if err != nil {
		return false, err
	}
	for _, r := range rules {
		if r.Mark == fwmark && r.Table == table {
			return true, nil
		}
	}
	return false, nil
}

// ReplaceRule add or replace rule in the routing table using a mark to indicate
// table. Used with BPF datapath to set mark and direct packets to route table.
func ReplaceRule(fwmark int, table int) error {
	exists, err := lookupRule(fwmark, table, netlink.FAMILY_V4)
	if err != nil {
		return err
	}
	if exists == true {
		return nil
	}
	return replaceRule(fwmark, table, netlink.FAMILY_V4)
}

// ReplaceRuleIPv6 add or replace IPv6 rule in the routing table using a mark to
// indicate table.
func ReplaceRuleIPv6(fwmark, table int) error {
	exists, err := lookupRule(fwmark, table, netlink.FAMILY_V6)
	if err != nil {
		return err
	}
	if exists == true {
		return nil
	}
	return replaceRule(fwmark, table, netlink.FAMILY_V6)
}

func replaceRule(fwmark, table, family int) error {
	rule := netlink.NewRule()
	rule.Mark = fwmark
	rule.Mask = linux_defaults.RouteMarkMask
	rule.Table = table
	rule.Family = family
	rule.Priority = 1
	return netlink.RuleAdd(rule)
}

// DeleteRule delete a mark based rule from the routing table.
func DeleteRule(fwmark int, table int) error {
	rule := netlink.NewRule()
	rule.Mark = fwmark
	rule.Table = table
	return netlink.RuleDel(rule)
}
<commit_msg>cilium: route, fix deleteRule to include mask and support IPv6<commit_after>// Copyright 2016-2018 Authors of Cilium
//
// 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.
//
// +build linux

package route

import (
	"fmt"
	"net"
	"time"

	"github.com/cilium/cilium/pkg/datapath/linux/linux_defaults"
	"github.com/cilium/cilium/pkg/mtu"

	"github.com/vishvananda/netlink"
)

const (
	// RouteReplaceMaxTries is the number of attempts the route will be
	// attempted to be added or updated in case the kernel returns an error
	RouteReplaceMaxTries = 10

	// RouteReplaceRetryInterval is the interval in which
	// RouteReplaceMaxTries attempts are attempted
	RouteReplaceRetryInterval = 100 * time.Millisecond

	// RTN_LOCAL is a route type used to indicate packet should be "routed"
	// locally and passed up the stack. Is used by IPSec to force encrypted
	// packets to pass through XFRM layer.
	RTN_LOCAL = 0x2
)

// getNetlinkRoute returns the route configuration as netlink.Route
func (r *Route) getNetlinkRoute() netlink.Route {
	rt := netlink.Route{
		Dst:      &r.Prefix,
		Src:      r.Local,
		MTU:      r.MTU,
		Protocol: r.Proto,
		Table:    r.Table,
		Type:     r.Type,
	}

	if r.Nexthop != nil {
		rt.Gw = *r.Nexthop
	}

	if r.Scope != netlink.SCOPE_UNIVERSE {
		rt.Scope = r.Scope
	} else if r.Scope == netlink.SCOPE_UNIVERSE && r.Type == RTN_LOCAL {
		rt.Scope = netlink.SCOPE_HOST
	}

	return rt
}

// getNexthopAsIPNet returns the nexthop of the route as IPNet
func (r *Route) getNexthopAsIPNet() *net.IPNet {
	if r.Nexthop == nil {
		return nil
	}

	if r.Nexthop.To4() != nil {
		return &net.IPNet{IP: *r.Nexthop, Mask: net.CIDRMask(32, 32)}
	}

	return &net.IPNet{IP: *r.Nexthop, Mask: net.CIDRMask(128, 128)}
}

func ipFamily(ip net.IP) int {
	if ip.To4() == nil {
		return netlink.FAMILY_V6
	}

	return netlink.FAMILY_V4
}

// Lookup attempts to find the linux route based on the route specification.
// If the route exists, the route is returned, otherwise an error is returned.
func Lookup(route Route) (*Route, error) {
	link, err := netlink.LinkByName(route.Device)
	if err != nil {
		return nil, fmt.Errorf("unable to find interface '%s' of route: %s", route.Device, err)
	}

	routeSpec := route.getNetlinkRoute()

	nlRoute := lookup(link, &routeSpec)
	if nlRoute == nil {
		return nil, nil
	}

	result := &Route{
		Local:   nlRoute.Src,
		Device:  link.Attrs().Name,
		MTU:     nlRoute.MTU,
		Scope:   nlRoute.Scope,
		Nexthop: &nlRoute.Gw,
	}

	if nlRoute.Dst != nil {
		result.Prefix = *nlRoute.Dst
	}

	return result, nil
}

// lookup finds a particular route as specified by the filter which points
// to the specified device. The filter route can have the following fields set:
//  - Dst
//  - LinkIndex
//  - Scope
//  - Gw
func lookup(link netlink.Link, route *netlink.Route) *netlink.Route {
	routes, err := netlink.RouteList(link, ipFamily(route.Dst.IP))
	if err != nil {
		return nil
	}

	for _, r := range routes {
		if r.Dst != nil && route.Dst == nil {
			continue
		}

		if route.Dst != nil && r.Dst == nil {
			continue
		}

		if route.Table != 0 && route.Table != r.Table {
			continue
		}

		aMaskLen, aMaskBits := r.Dst.Mask.Size()
		bMaskLen, bMaskBits := route.Dst.Mask.Size()
		if r.Scope == route.Scope &&
			aMaskLen == bMaskLen && aMaskBits == bMaskBits &&
			r.Dst.IP.Equal(route.Dst.IP) && r.Gw.Equal(route.Gw) {
			return &r
		}
	}

	return nil
}

func createNexthopRoute(link netlink.Link, routerNet *net.IPNet) *netlink.Route {
	// This is the L2 route which makes router IP available behind the
	// interface.
	rt := &netlink.Route{
		LinkIndex: link.Attrs().Index,
		Dst:       routerNet,
	}

	// Known issue: scope for IPv6 routes is not propagated correctly. If
	// we set the scope here, lookup() will be unable to identify the route
	// again and we will continously re-add the route
	if routerNet.IP.To4() != nil {
		rt.Scope = netlink.SCOPE_LINK
	}

	return rt
}

// replaceNexthopRoute verifies that the L2 route for the router IP which is
// used as nexthop for all node routes is properly installed. If unavailable or
// incorrect, it will be replaced with the proper L2 route.
func replaceNexthopRoute(link netlink.Link, routerNet *net.IPNet) (bool, error) {
	route := createNexthopRoute(link, routerNet)
	if lookup(link, route) == nil {
		if err := netlink.RouteReplace(route); err != nil {
			return false, fmt.Errorf("unable to add L2 nexthop route: %s", err)
		}

		return true, nil
	}

	return false, nil
}

// deleteNexthopRoute deletes
func deleteNexthopRoute(link netlink.Link, routerNet *net.IPNet) error {
	route := createNexthopRoute(link, routerNet)
	if err := netlink.RouteDel(route); err != nil {
		return fmt.Errorf("unable to delete L2 nexthop route: %s", err)
	}

	return nil
}

// Upsert adds or updates a Linux kernel route. The route described can be in
// the following two forms:
//
// direct:
//   prefix dev foo
//
// nexthop:
//   prefix via nexthop dev foo
//
// If a nexthop route is specified, this function will check whether a direct
// route to the nexthop exists and add if required. This means that the
// following two routes will exist afterwards:
//
//   nexthop dev foo
//   prefix via nexthop dev foo
//
// Due to a bug in the Linux kernel, the prefix route is attempted to be
// updated RouteReplaceMaxTries with an interval of RouteReplaceRetryInterval.
// This is a workaround for a race condition in which the direct route to the
// nexthop is not available immediately and the prefix route can fail with
// EINVAL if the Netlink calls are issued in short order.
//
// An error is returned if the route can not be added or updated.
func Upsert(route Route, mtuConfig mtu.Configuration) (bool, error) {
	var nexthopRouteCreated bool

	link, err := netlink.LinkByName(route.Device)
	if err != nil {
		return false, fmt.Errorf("unable to lookup interface %s: %s", route.Device, err)
	}

	routerNet := route.getNexthopAsIPNet()
	if routerNet != nil {
		if _, err := replaceNexthopRoute(link, routerNet); err != nil {
			return false, fmt.Errorf("unable to add nexthop route: %s", err)
		}

		nexthopRouteCreated = true
	}

	routeSpec := route.getNetlinkRoute()
	routeSpec.LinkIndex = link.Attrs().Index

	if routeSpec.MTU != 0 {
		// If the route includes the local address, then the route is for
		// local containers and we can use a high MTU for transmit. Otherwise,
		// it needs to be able to fit within the MTU of tunnel devices.
		if route.Prefix.Contains(route.Local) {
			routeSpec.MTU = mtuConfig.GetDeviceMTU()
		} else {
			routeSpec.MTU = mtuConfig.GetRouteMTU()
		}
	}

	if lookup(link, &routeSpec) == nil {
		err := fmt.Errorf("routeReplace not called yet")

		// Workaround: See description of this function
		for i := 0; err != nil && i < RouteReplaceMaxTries; i++ {
			err = netlink.RouteReplace(&routeSpec)
			if err == nil {
				break
			}
			time.Sleep(RouteReplaceRetryInterval)
		}

		if err != nil {
			if nexthopRouteCreated {
				deleteNexthopRoute(link, routerNet)
			}
			return false, err
		}

		return true, nil
	}

	return false, nil
}

// Delete deletes a Linux route. An error is returned if the route does not
// exist or if the route could not be deleted.
func Delete(route Route) error {
	link, err := netlink.LinkByName(route.Device)
	if err != nil {
		return fmt.Errorf("unable to lookup interface %s: %s", route.Device, err)
	}

	// Deletion of routes with Nexthop or Local set fails for IPv6.
	// Therefore do not use getNetlinkRoute().
	routeSpec := netlink.Route{
		Dst:       &route.Prefix,
		LinkIndex: link.Attrs().Index,
	}

	// Scope can only be specified for IPv4
	if route.Prefix.IP.To4() != nil {
		routeSpec.Scope = route.Scope
	}

	if err := netlink.RouteDel(&routeSpec); err != nil {
		return err
	}

	return nil
}

func lookupRule(fwmark, table, family int) (bool, error) {
	rules, err := netlink.RuleList(family)
	if err != nil {
		return false, err
	}
	for _, r := range rules {
		if r.Mark == fwmark && r.Table == table {
			return true, nil
		}
	}
	return false, nil
}

// ReplaceRule add or replace rule in the routing table using a mark to indicate
// table. Used with BPF datapath to set mark and direct packets to route table.
func ReplaceRule(fwmark int, table int) error {
	exists, err := lookupRule(fwmark, table, netlink.FAMILY_V4)
	if err != nil {
		return err
	}
	if exists == true {
		return nil
	}
	return replaceRule(fwmark, table, netlink.FAMILY_V4)
}

// ReplaceRuleIPv6 add or replace IPv6 rule in the routing table using a mark to
// indicate table.
func ReplaceRuleIPv6(fwmark, table int) error {
	exists, err := lookupRule(fwmark, table, netlink.FAMILY_V6)
	if err != nil {
		return err
	}
	if exists == true {
		return nil
	}
	return replaceRule(fwmark, table, netlink.FAMILY_V6)
}

func replaceRule(fwmark, table, family int) error {
	rule := netlink.NewRule()
	rule.Mark = fwmark
	rule.Mask = linux_defaults.RouteMarkMask
	rule.Table = table
	rule.Family = family
	rule.Priority = 1
	return netlink.RuleAdd(rule)
}

// DeleteRule delete a mark based rule from the routing table.
func DeleteRule(fwmark int, table int) error {
	rule := netlink.NewRule()
	rule.Mark = fwmark
	rule.Mask = linux_defaults.RouteMarkMask
	rule.Table = table
	rule.Family = netlink.FAMILY_V4
	return netlink.RuleDel(rule)
}

// DeleteRuleIPv6 delete a mark based IPv6 rule from the routing table.
func DeleteRuleIPv6(fwmark int, table int) error {
	rule := netlink.NewRule()
	rule.Mark = fwmark
	rule.Table = table
	rule.Family = netlink.FAMILY_V6
	return netlink.RuleDel(rule)
}
<|endoftext|>


<commit_before>// Copyright 2016-2018 Authors of Cilium
//
// 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.
//
// +build linux

package route

import (
	"fmt"
	"net"
	"time"

	"github.com/vishvananda/netlink"
)

const (
	// RouteReplaceMaxTries is the number of attempts the route will be
	// attempted to be added or updated in case the kernel returns an error
	RouteReplaceMaxTries = 10

	// RouteReplaceRetryInterval is the interval in which
	// RouteReplaceMaxTries attempts are attempted
	RouteReplaceRetryInterval = 100 * time.Millisecond

	// RTN_LOCAL is a route type used to indicate packet should be "routed"
	// locally and passed up the stack. Is used by IPSec to force encrypted
	// packets to pass through XFRM layer.
	RTN_LOCAL = 0x2

	// MainTable is Linux's default routing table
	MainTable = 254

	// EncryptRouteProtocol for Encryption specific routes
	EncryptRouteProtocol = 192
)

// getNetlinkRoute returns the route configuration as netlink.Route
func (r *Route) getNetlinkRoute() netlink.Route {
	rt := netlink.Route{
		Dst:      &r.Prefix,
		Src:      r.Local,
		MTU:      r.MTU,
		Protocol: r.Proto,
		Table:    r.Table,
		Type:     r.Type,
	}

	if r.Nexthop != nil {
		rt.Gw = *r.Nexthop
	}

	if r.Scope != netlink.SCOPE_UNIVERSE {
		rt.Scope = r.Scope
	} else if r.Scope == netlink.SCOPE_UNIVERSE && r.Type == RTN_LOCAL {
		rt.Scope = netlink.SCOPE_HOST
	}

	return rt
}

// getNexthopAsIPNet returns the nexthop of the route as IPNet
func (r *Route) getNexthopAsIPNet() *net.IPNet {
	if r.Nexthop == nil {
		return nil
	}

	if r.Nexthop.To4() != nil {
		return &net.IPNet{IP: *r.Nexthop, Mask: net.CIDRMask(32, 32)}
	}

	return &net.IPNet{IP: *r.Nexthop, Mask: net.CIDRMask(128, 128)}
}

func ipFamily(ip net.IP) int {
	if ip.To4() == nil {
		return netlink.FAMILY_V6
	}

	return netlink.FAMILY_V4
}

// Lookup attempts to find the linux route based on the route specification.
// If the route exists, the route is returned, otherwise an error is returned.
func Lookup(route Route) (*Route, error) {
	link, err := netlink.LinkByName(route.Device)
	if err != nil {
		return nil, fmt.Errorf("unable to find interface '%s' of route: %s", route.Device, err)
	}

	routeSpec := route.getNetlinkRoute()
	routeSpec.LinkIndex = link.Attrs().Index

	nlRoute := lookup(&routeSpec)
	if nlRoute == nil {
		return nil, nil
	}

	result := &Route{
		Local:   nlRoute.Src,
		Device:  link.Attrs().Name,
		MTU:     nlRoute.MTU,
		Scope:   nlRoute.Scope,
		Nexthop: &nlRoute.Gw,
	}

	if nlRoute.Dst != nil {
		result.Prefix = *nlRoute.Dst
	}

	return result, nil
}

// lookup finds a particular route as specified by the filter which points
// to the specified device. The filter route can have the following fields set:
//  - Dst
//  - LinkIndex
//  - Scope
//  - Gw
func lookup(route *netlink.Route) *netlink.Route {
	var filter uint64
	if route.Dst != nil {
		filter |= netlink.RT_FILTER_DST
	}
	if route.Table != 0 {
		filter |= netlink.RT_FILTER_TABLE
	}
	if route.Scope != 0 {
		filter |= netlink.RT_FILTER_SCOPE
	}
	if route.Gw != nil {
		filter |= netlink.RT_FILTER_GW
	}
	if route.LinkIndex != 0 {
		filter |= netlink.RT_FILTER_OIF
	}

	routes, err := netlink.RouteListFiltered(ipFamily(route.Dst.IP), route, filter)
	if err != nil {
		return nil
	}

	for _, r := range routes {
		if r.Dst != nil && route.Dst == nil {
			continue
		}

		if route.Dst != nil && r.Dst == nil {
			continue
		}

		if route.Table != 0 && route.Table != r.Table {
			continue
		}

		aMaskLen, aMaskBits := r.Dst.Mask.Size()
		bMaskLen, bMaskBits := route.Dst.Mask.Size()
		if r.Scope == route.Scope &&
			aMaskLen == bMaskLen && aMaskBits == bMaskBits &&
			r.Dst.IP.Equal(route.Dst.IP) && r.Gw.Equal(route.Gw) {
			return &r
		}
	}

	return nil
}

func createNexthopRoute(route Route, link netlink.Link, routerNet *net.IPNet) *netlink.Route {
	// This is the L2 route which makes router IP available behind the
	// interface.
	rt := &netlink.Route{
		LinkIndex: link.Attrs().Index,
		Dst:       routerNet,
		Table:     route.Table,
	}

	// Known issue: scope for IPv6 routes is not propagated correctly. If
	// we set the scope here, lookup() will be unable to identify the route
	// again and we will continuously re-add the route
	if routerNet.IP.To4() != nil {
		rt.Scope = netlink.SCOPE_LINK
	}

	return rt
}

// replaceNexthopRoute verifies that the L2 route for the router IP which is
// used as nexthop for all node routes is properly installed. If unavailable or
// incorrect, it will be replaced with the proper L2 route.
func replaceNexthopRoute(route Route, link netlink.Link, routerNet *net.IPNet) (bool, error) {
	if err := netlink.RouteReplace(createNexthopRoute(route, link, routerNet)); err != nil {
		return false, fmt.Errorf("unable to add L2 nexthop route: %s", err)
	}

	return true, nil
}

// deleteNexthopRoute deletes
func deleteNexthopRoute(route Route, link netlink.Link, routerNet *net.IPNet) error {
	if err := netlink.RouteDel(createNexthopRoute(route, link, routerNet)); err != nil {
		return fmt.Errorf("unable to delete L2 nexthop route: %s", err)
	}

	return nil
}

// Upsert adds or updates a Linux kernel route. The route described can be in
// the following two forms:
//
// direct:
//   prefix dev foo
//
// nexthop:
//   prefix via nexthop dev foo
//
// If a nexthop route is specified, this function will check whether a direct
// route to the nexthop exists and add if required. This means that the
// following two routes will exist afterwards:
//
//   nexthop dev foo
//   prefix via nexthop dev foo
//
// Due to a bug in the Linux kernel, the prefix route is attempted to be
// updated RouteReplaceMaxTries with an interval of RouteReplaceRetryInterval.
// This is a workaround for a race condition in which the direct route to the
// nexthop is not available immediately and the prefix route can fail with
// EINVAL if the Netlink calls are issued in short order.
//
// An error is returned if the route can not be added or updated.
func Upsert(route Route) (bool, error) {
	var nexthopRouteCreated bool

	link, err := netlink.LinkByName(route.Device)
	if err != nil {
		return false, fmt.Errorf("unable to lookup interface %s: %s", route.Device, err)
	}

	routerNet := route.getNexthopAsIPNet()
	if routerNet != nil {
		if _, err := replaceNexthopRoute(route, link, routerNet); err != nil {
			return false, fmt.Errorf("unable to add nexthop route: %s", err)
		}

		nexthopRouteCreated = true
	}

	routeSpec := route.getNetlinkRoute()
	routeSpec.LinkIndex = link.Attrs().Index

	err = fmt.Errorf("routeReplace not called yet")

	// Workaround: See description of this function
	for i := 0; err != nil && i < RouteReplaceMaxTries; i++ {
		err = netlink.RouteReplace(&routeSpec)
		if err == nil {
			break
		}
		time.Sleep(RouteReplaceRetryInterval)
	}

	if err != nil {
		if nexthopRouteCreated {
			deleteNexthopRoute(route, link, routerNet)
		}
		return false, err
	}

	return true, nil
}

// Delete deletes a Linux route. An error is returned if the route does not
// exist or if the route could not be deleted.
func Delete(route Route) error {
	link, err := netlink.LinkByName(route.Device)
	if err != nil {
		return fmt.Errorf("unable to lookup interface %s: %s", route.Device, err)
	}

	// Deletion of routes with Nexthop or Local set fails for IPv6.
	// Therefore do not use getNetlinkRoute().
	routeSpec := netlink.Route{
		Dst:       &route.Prefix,
		LinkIndex: link.Attrs().Index,
		Table:     route.Table,
	}

	// Scope can only be specified for IPv4
	if route.Prefix.IP.To4() != nil {
		routeSpec.Scope = route.Scope
	}

	if err := netlink.RouteDel(&routeSpec); err != nil {
		return err
	}

	return nil
}

// Rule is the specification of an IP routing rule
type Rule struct {
	// Priority is the routing rule priority
	Priority int

	// Mark is the skb mark that needs to match
	Mark int

	// Mask is the mask to apply to the skb mark before matching the Mark
	// field
	Mask int

	// From is the source address selector
	From *net.IPNet

	// To is the destination address selector
	To *net.IPNet

	// Table is the routing table to look up if the rule matches
	Table int
}

func lookupRule(spec Rule, family int) (bool, error) {
	rules, err := netlink.RuleList(family)
	if err != nil {
		return false, err
	}
	for _, r := range rules {
		if spec.Priority != 0 && spec.Priority != r.Priority {
			continue
		}

		if spec.From != nil && (r.Src == nil || r.Src.String() != spec.From.String()) {
			continue
		}

		if spec.To != nil && (r.Dst == nil || r.Dst.String() != spec.To.String()) {
			continue
		}

		if spec.Mark != 0 && r.Mark != spec.Mark {
			continue
		}

		if r.Table == spec.Table {
			return true, nil
		}
	}
	return false, nil
}

// ReplaceRule add or replace rule in the routing table using a mark to indicate
// table. Used with BPF datapath to set mark and direct packets to route table.
func ReplaceRule(spec Rule) error {
	exists, err := lookupRule(spec, netlink.FAMILY_V4)
	if err != nil {
		return err
	}
	if exists == true {
		return nil
	}
	return replaceRule(spec, netlink.FAMILY_V4)
}

// ReplaceRuleIPv6 add or replace IPv6 rule in the routing table using a mark to
// indicate table.
func ReplaceRuleIPv6(spec Rule) error {
	exists, err := lookupRule(spec, netlink.FAMILY_V6)
	if err != nil {
		return err
	}
	if exists == true {
		return nil
	}
	return replaceRule(spec, netlink.FAMILY_V6)
}

func replaceRule(spec Rule, family int) error {
	rule := netlink.NewRule()
	rule.Mark = spec.Mark
	rule.Mask = spec.Mask
	rule.Table = spec.Table
	rule.Family = family
	rule.Priority = spec.Priority
	rule.Src = spec.From
	rule.Dst = spec.To
	return netlink.RuleAdd(rule)
}

// DeleteRule delete a mark based rule from the routing table.
func DeleteRule(spec Rule) error {
	rule := netlink.NewRule()
	rule.Mark = spec.Mark
	rule.Mask = spec.Mask
	rule.Table = spec.Table
	rule.Priority = spec.Priority
	rule.Src = spec.From
	rule.Dst = spec.To
	rule.Family = netlink.FAMILY_V4
	return netlink.RuleDel(rule)
}

// DeleteRuleIPv6 delete a mark based IPv6 rule from the routing table.
func DeleteRuleIPv6(spec Rule) error {
	rule := netlink.NewRule()
	rule.Mark = spec.Mark
	rule.Mask = spec.Mask
	rule.Table = spec.Table
	rule.Priority = spec.Priority
	rule.Src = spec.From
	rule.Dst = spec.To
	rule.Family = netlink.FAMILY_V6
	return netlink.RuleDel(rule)
}
<commit_msg>pkg/datapath/linux/route: reduce duplicate code<commit_after>// Copyright 2016-2018 Authors of Cilium
//
// 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.
//
// +build linux

package route

import (
	"fmt"
	"net"
	"time"

	"github.com/vishvananda/netlink"
)

const (
	// RouteReplaceMaxTries is the number of attempts the route will be
	// attempted to be added or updated in case the kernel returns an error
	RouteReplaceMaxTries = 10

	// RouteReplaceRetryInterval is the interval in which
	// RouteReplaceMaxTries attempts are attempted
	RouteReplaceRetryInterval = 100 * time.Millisecond

	// RTN_LOCAL is a route type used to indicate packet should be "routed"
	// locally and passed up the stack. Is used by IPSec to force encrypted
	// packets to pass through XFRM layer.
	RTN_LOCAL = 0x2

	// MainTable is Linux's default routing table
	MainTable = 254

	// EncryptRouteProtocol for Encryption specific routes
	EncryptRouteProtocol = 192
)

// getNetlinkRoute returns the route configuration as netlink.Route
func (r *Route) getNetlinkRoute() netlink.Route {
	rt := netlink.Route{
		Dst:      &r.Prefix,
		Src:      r.Local,
		MTU:      r.MTU,
		Protocol: r.Proto,
		Table:    r.Table,
		Type:     r.Type,
	}

	if r.Nexthop != nil {
		rt.Gw = *r.Nexthop
	}

	if r.Scope != netlink.SCOPE_UNIVERSE {
		rt.Scope = r.Scope
	} else if r.Scope == netlink.SCOPE_UNIVERSE && r.Type == RTN_LOCAL {
		rt.Scope = netlink.SCOPE_HOST
	}

	return rt
}

// getNexthopAsIPNet returns the nexthop of the route as IPNet
func (r *Route) getNexthopAsIPNet() *net.IPNet {
	if r.Nexthop == nil {
		return nil
	}

	if r.Nexthop.To4() != nil {
		return &net.IPNet{IP: *r.Nexthop, Mask: net.CIDRMask(32, 32)}
	}

	return &net.IPNet{IP: *r.Nexthop, Mask: net.CIDRMask(128, 128)}
}

func ipFamily(ip net.IP) int {
	if ip.To4() == nil {
		return netlink.FAMILY_V6
	}

	return netlink.FAMILY_V4
}

// Lookup attempts to find the linux route based on the route specification.
// If the route exists, the route is returned, otherwise an error is returned.
func Lookup(route Route) (*Route, error) {
	link, err := netlink.LinkByName(route.Device)
	if err != nil {
		return nil, fmt.Errorf("unable to find interface '%s' of route: %s", route.Device, err)
	}

	routeSpec := route.getNetlinkRoute()
	routeSpec.LinkIndex = link.Attrs().Index

	nlRoute := lookup(&routeSpec)
	if nlRoute == nil {
		return nil, nil
	}

	result := &Route{
		Local:   nlRoute.Src,
		Device:  link.Attrs().Name,
		MTU:     nlRoute.MTU,
		Scope:   nlRoute.Scope,
		Nexthop: &nlRoute.Gw,
	}

	if nlRoute.Dst != nil {
		result.Prefix = *nlRoute.Dst
	}

	return result, nil
}

// lookup finds a particular route as specified by the filter which points
// to the specified device. The filter route can have the following fields set:
//  - Dst
//  - LinkIndex
//  - Scope
//  - Gw
func lookup(route *netlink.Route) *netlink.Route {
	var filter uint64
	if route.Dst != nil {
		filter |= netlink.RT_FILTER_DST
	}
	if route.Table != 0 {
		filter |= netlink.RT_FILTER_TABLE
	}
	if route.Scope != 0 {
		filter |= netlink.RT_FILTER_SCOPE
	}
	if route.Gw != nil {
		filter |= netlink.RT_FILTER_GW
	}
	if route.LinkIndex != 0 {
		filter |= netlink.RT_FILTER_OIF
	}

	routes, err := netlink.RouteListFiltered(ipFamily(route.Dst.IP), route, filter)
	if err != nil {
		return nil
	}

	for _, r := range routes {
		if r.Dst != nil && route.Dst == nil {
			continue
		}

		if route.Dst != nil && r.Dst == nil {
			continue
		}

		if route.Table != 0 && route.Table != r.Table {
			continue
		}

		aMaskLen, aMaskBits := r.Dst.Mask.Size()
		bMaskLen, bMaskBits := route.Dst.Mask.Size()
		if r.Scope == route.Scope &&
			aMaskLen == bMaskLen && aMaskBits == bMaskBits &&
			r.Dst.IP.Equal(route.Dst.IP) && r.Gw.Equal(route.Gw) {
			return &r
		}
	}

	return nil
}

func createNexthopRoute(route Route, link netlink.Link, routerNet *net.IPNet) *netlink.Route {
	// This is the L2 route which makes router IP available behind the
	// interface.
	rt := &netlink.Route{
		LinkIndex: link.Attrs().Index,
		Dst:       routerNet,
		Table:     route.Table,
	}

	// Known issue: scope for IPv6 routes is not propagated correctly. If
	// we set the scope here, lookup() will be unable to identify the route
	// again and we will continuously re-add the route
	if routerNet.IP.To4() != nil {
		rt.Scope = netlink.SCOPE_LINK
	}

	return rt
}

// replaceNexthopRoute verifies that the L2 route for the router IP which is
// used as nexthop for all node routes is properly installed. If unavailable or
// incorrect, it will be replaced with the proper L2 route.
func replaceNexthopRoute(route Route, link netlink.Link, routerNet *net.IPNet) (bool, error) {
	if err := netlink.RouteReplace(createNexthopRoute(route, link, routerNet)); err != nil {
		return false, fmt.Errorf("unable to add L2 nexthop route: %s", err)
	}

	return true, nil
}

// deleteNexthopRoute deletes
func deleteNexthopRoute(route Route, link netlink.Link, routerNet *net.IPNet) error {
	if err := netlink.RouteDel(createNexthopRoute(route, link, routerNet)); err != nil {
		return fmt.Errorf("unable to delete L2 nexthop route: %s", err)
	}

	return nil
}

// Upsert adds or updates a Linux kernel route. The route described can be in
// the following two forms:
//
// direct:
//   prefix dev foo
//
// nexthop:
//   prefix via nexthop dev foo
//
// If a nexthop route is specified, this function will check whether a direct
// route to the nexthop exists and add if required. This means that the
// following two routes will exist afterwards:
//
//   nexthop dev foo
//   prefix via nexthop dev foo
//
// Due to a bug in the Linux kernel, the prefix route is attempted to be
// updated RouteReplaceMaxTries with an interval of RouteReplaceRetryInterval.
// This is a workaround for a race condition in which the direct route to the
// nexthop is not available immediately and the prefix route can fail with
// EINVAL if the Netlink calls are issued in short order.
//
// An error is returned if the route can not be added or updated.
func Upsert(route Route) (bool, error) {
	var nexthopRouteCreated bool

	link, err := netlink.LinkByName(route.Device)
	if err != nil {
		return false, fmt.Errorf("unable to lookup interface %s: %s", route.Device, err)
	}

	routerNet := route.getNexthopAsIPNet()
	if routerNet != nil {
		if _, err := replaceNexthopRoute(route, link, routerNet); err != nil {
			return false, fmt.Errorf("unable to add nexthop route: %s", err)
		}

		nexthopRouteCreated = true
	}

	routeSpec := route.getNetlinkRoute()
	routeSpec.LinkIndex = link.Attrs().Index

	err = fmt.Errorf("routeReplace not called yet")

	// Workaround: See description of this function
	for i := 0; err != nil && i < RouteReplaceMaxTries; i++ {
		err = netlink.RouteReplace(&routeSpec)
		if err == nil {
			break
		}
		time.Sleep(RouteReplaceRetryInterval)
	}

	if err != nil {
		if nexthopRouteCreated {
			deleteNexthopRoute(route, link, routerNet)
		}
		return false, err
	}

	return true, nil
}

// Delete deletes a Linux route. An error is returned if the route does not
// exist or if the route could not be deleted.
func Delete(route Route) error {
	link, err := netlink.LinkByName(route.Device)
	if err != nil {
		return fmt.Errorf("unable to lookup interface %s: %s", route.Device, err)
	}

	// Deletion of routes with Nexthop or Local set fails for IPv6.
	// Therefore do not use getNetlinkRoute().
	routeSpec := netlink.Route{
		Dst:       &route.Prefix,
		LinkIndex: link.Attrs().Index,
		Table:     route.Table,
	}

	// Scope can only be specified for IPv4
	if route.Prefix.IP.To4() != nil {
		routeSpec.Scope = route.Scope
	}

	if err := netlink.RouteDel(&routeSpec); err != nil {
		return err
	}

	return nil
}

// Rule is the specification of an IP routing rule
type Rule struct {
	// Priority is the routing rule priority
	Priority int

	// Mark is the skb mark that needs to match
	Mark int

	// Mask is the mask to apply to the skb mark before matching the Mark
	// field
	Mask int

	// From is the source address selector
	From *net.IPNet

	// To is the destination address selector
	To *net.IPNet

	// Table is the routing table to look up if the rule matches
	Table int
}

func lookupRule(spec Rule, family int) (bool, error) {
	rules, err := netlink.RuleList(family)
	if err != nil {
		return false, err
	}
	for _, r := range rules {
		if spec.Priority != 0 && spec.Priority != r.Priority {
			continue
		}

		if spec.From != nil && (r.Src == nil || r.Src.String() != spec.From.String()) {
			continue
		}

		if spec.To != nil && (r.Dst == nil || r.Dst.String() != spec.To.String()) {
			continue
		}

		if spec.Mark != 0 && r.Mark != spec.Mark {
			continue
		}

		if r.Table == spec.Table {
			return true, nil
		}
	}
	return false, nil
}

// ReplaceRule add or replace rule in the routing table using a mark to indicate
// table. Used with BPF datapath to set mark and direct packets to route table.
func ReplaceRule(spec Rule) error {
	return replaceRule(spec, netlink.FAMILY_V4)
}

// ReplaceRuleIPv6 add or replace IPv6 rule in the routing table using a mark to
// indicate table.
func ReplaceRuleIPv6(spec Rule) error {
	return replaceRule(spec, netlink.FAMILY_V6)
}

func replaceRule(spec Rule, family int) error {
	exists, err := lookupRule(spec, family)
	if err != nil {
		return err
	}
	if exists == true {
		return nil
	}
	rule := netlink.NewRule()
	rule.Mark = spec.Mark
	rule.Mask = spec.Mask
	rule.Table = spec.Table
	rule.Family = family
	rule.Priority = spec.Priority
	rule.Src = spec.From
	rule.Dst = spec.To
	return netlink.RuleAdd(rule)
}

// DeleteRule delete a mark based rule from the routing table.
func DeleteRule(spec Rule) error {
	return deleteRule(spec, netlink.FAMILY_V4)
}

// DeleteRuleIPv6 delete a mark based IPv6 rule from the routing table.
func DeleteRuleIPv6(spec Rule) error {
	return deleteRule(spec, netlink.FAMILY_V6)
}

func deleteRule(spec Rule, family int) error {
	rule := netlink.NewRule()
	rule.Mark = spec.Mark
	rule.Mask = spec.Mask
	rule.Table = spec.Table
	rule.Priority = spec.Priority
	rule.Src = spec.From
	rule.Dst = spec.To
	rule.Family = family
	return netlink.RuleDel(rule)
}
<|endoftext|>