diff --git a/src/README.md b/src/README.md index 818f238961..6dcbb3955e 100644 --- a/src/README.md +++ b/src/README.md @@ -294,6 +294,16 @@ Predicate used to test if a link is full. Returns true if *link* is full and false otherwise. +— Function **link.nreadable** *link* + +Returns the number of packets on *link*. + + +— Function **link.nwriteable** *link* + +Returns the remaining number of packets that fit onto *link*. + + — Function **link.receive** *link* Returns the next available packet (and advances the read cursor) on @@ -323,7 +333,7 @@ Returns a structure holding ring statistics for the *link*: ## Packet (core.packet) -A *packet* is an FFI object of type `packet.packet_t` representing a network +A *packet* is an FFI object of type `struct packet` representing a network packet that is currently being processed. The packet is used to explicitly manage the life cycle of the packet. Packets are explicitly allocated and freed by using `packet.allocate` and `packet.free`. When a packet is received using @@ -335,7 +345,7 @@ freed. The number of allocatable packets is limited by the size of the underlying “freelist”, e.g. a pool of unused packet objects from and to which packets are allocated and freed. -— Ctype **packet.packet_t** +— Type **struct packet** ``` struct packet { @@ -387,7 +397,7 @@ or equal to `length` of *packet*. — Function **packet.shiftright** *packet*, *length* -Move *packet* payload to the right by *length* bytes, growing *packet* by +Moves *packet* payload to the right by *length* bytes, growing *packet* by *length*. The sum of *length* and `length` of *packet* must be less than or equal to `packet.max_payload`. @@ -399,6 +409,10 @@ Allocate packet and fill it with *length* bytes from *pointer*. Allocate packet and fill it with the contents of *string*. +— Function **packet.clone_to_memory* *pointer* *packet* + +Creates an exact copy of at memory pointed to by *pointer*. *Pointer* must +point to a `packet.packet_t`. ## Memory (core.memory) @@ -471,7 +485,7 @@ Returns a pointer to the mapped object. — Function **shm.exists** *name* -Checks whether shared object *name* exists. +Returns a true value if shared object by *name* exists. — Function **shm.unmap** *pointer* diff --git a/src/apps/basic/basic_apps.lua b/src/apps/basic/basic_apps.lua index 10ec6e1c90..94eb669272 100644 --- a/src/apps/basic/basic_apps.lua +++ b/src/apps/basic/basic_apps.lua @@ -21,7 +21,7 @@ end function Source:pull () for _, o in ipairs(self.output) do - for i = 1, link.nwritable(o) do + for i = 1, engine.pull_npackets do transmit(o, packet.clone(self.packet)) end end @@ -41,7 +41,7 @@ end function Join:push () for _, inport in ipairs(self.input) do - for n = 1,math.min(link.nreadable(inport), link.nwritable(self.output.out)) do + while not link.empty(inport) do transmit(self.output.out, receive(inport)) end end @@ -60,7 +60,7 @@ end function Split:push () for _, i in ipairs(self.input) do for _, o in ipairs(self.output) do - for _ = 1, math.min(link.nreadable(i), link.nwritable(o)) do + for _ = 1, link.nreadable(i) do transmit(o, receive(i)) end end @@ -93,16 +93,11 @@ function Tee:new () end function Tee:push () - noutputs = #self.output + local noutputs = #self.output if noutputs > 0 then - local maxoutput = link.max - for _, o in ipairs(self.output) do - maxoutput = math.min(maxoutput, link.nwritable(o)) - end for _, i in ipairs(self.input) do - for _ = 1, math.min(link.nreadable(i), maxoutput) do + for _ = 1, link.nreadable(i) do local p = receive(i) - maxoutput = maxoutput - 1 do local output = self.output for k = 1, #output do transmit(output[k], k == #output and p or packet.clone(p)) @@ -122,7 +117,7 @@ function Repeater:new () {__index=Repeater}) end -function Repeater:push () +function Repeater:pull () local i, o = self.input.input, self.output.output for _ = 1, link.nreadable(i) do local p = receive(i) @@ -130,7 +125,7 @@ function Repeater:push () end local npackets = #self.packets if npackets > 0 then - for i = 1, link.nwritable(o) do + for i = 1, engine.pull_npackets do assert(self.packets[self.index]) transmit(o, packet.clone(self.packets[self.index])) self.index = (self.index % npackets) + 1 diff --git a/src/apps/intel/intel1g.lua b/src/apps/intel/intel1g.lua index 8253d28fc2..8b83eb310a 100644 --- a/src/apps/intel/intel1g.lua +++ b/src/apps/intel/intel1g.lua @@ -59,7 +59,7 @@ function Intel1g:new(conf) local txq = conf.txqueue or 0 local rxq = conf.rxqueue or 0 local ndesc = conf.ndescriptors or 512 - local rxburst = conf.rxburst or 128 + local rxburst = conf.rxburst or engine.pull_npackets -- 8.1.3 Register Summary, p.359 local r = {} @@ -589,12 +589,7 @@ function Intel1g:new(conf) while limit > 0 and can_receive() do limit = limit - 1 if lo then -- a link connects NIC to a sink - if not link.full(lo) then -- from SolarFlareNic:pull() - link.transmit(lo, receive()) - else - counters.pullTxLinkFull= counters.pullTxLinkFull +1 - packet.free(receive()) - end + link.transmit(lo, receive()) else counters.pullNoTxLink= counters.pullNoTxLink +1 packet.free(receive()) diff --git a/src/apps/intel/intel_app.lua b/src/apps/intel/intel_app.lua index 47b8b67cd5..f88ac272f9 100644 --- a/src/apps/intel/intel_app.lua +++ b/src/apps/intel/intel_app.lua @@ -12,7 +12,7 @@ local pci = require("lib.hardware.pci") local register = require("lib.hardware.register") local macaddress = require("lib.macaddress") local intel10g = require("apps.intel.intel10g") -local receive, transmit, full, empty = link.receive, link.transmit, link.full, link.empty +local receive, transmit, empty = link.receive, link.transmit, link.empty Intel82599 = {} Intel82599.__index = Intel82599 @@ -133,7 +133,7 @@ function Intel82599:pull () local l = self.output.tx if l == nil then return end self.dev:sync_receive() - for i=1,128 do + for i = 1, engine.pull_npackets do if not self.dev:can_receive() then break end transmit(l, self.dev:receive()) end diff --git a/src/apps/ipv6/nd_light.lua b/src/apps/ipv6/nd_light.lua index 7d00b8ff6d..e146e3a0ac 100644 --- a/src/apps/ipv6/nd_light.lua +++ b/src/apps/ipv6/nd_light.lua @@ -308,7 +308,7 @@ function nd_light:push () local l_in = self.input.south local l_out = self.output.north local l_reply = self.output.south - while not link.empty(l_in) and not link.full(l_out) do + while not link.empty(l_in) do local p = cache.p p[0] = link.receive(l_in) local status = from_south(self, p) @@ -327,7 +327,7 @@ function nd_light:push () l_in = self.input.north l_out = self.output.south - while not link.empty(l_in) and not link.full(l_out) do + while not link.empty(l_in) do if not self._eth_header then -- Drop packets until ND for the next-hop -- has completed. diff --git a/src/apps/ipv6/ns_responder.lua b/src/apps/ipv6/ns_responder.lua index e61b575a7d..62d3c1a627 100644 --- a/src/apps/ipv6/ns_responder.lua +++ b/src/apps/ipv6/ns_responder.lua @@ -88,7 +88,7 @@ function ns_responder:push() local l_in = self.input.north local l_out = self.output.south if l_in and l_out then - while not link.empty(l_in) and not link.full(l_out) do + while not link.empty(l_in) do -- Pass everything on north -> south link.transmit(l_out, link.receive(l_in)) end @@ -96,7 +96,7 @@ function ns_responder:push() l_in = self.input.south l_out = self.output.north local l_reply = self.output.south - while not link.empty(l_in) and not link.full(l_out) do + while not link.empty(l_in) do local p = link.receive(l_in) local status = process(self, p) if status == nil then diff --git a/src/apps/keyed_ipv6_tunnel/tunnel.lua b/src/apps/keyed_ipv6_tunnel/tunnel.lua index bf339e3083..9702a67ecc 100644 --- a/src/apps/keyed_ipv6_tunnel/tunnel.lua +++ b/src/apps/keyed_ipv6_tunnel/tunnel.lua @@ -187,7 +187,7 @@ function SimpleKeyedTunnel:push() local l_out = self.output.encapsulated assert(l_in and l_out) - while not link.empty(l_in) and not link.full(l_out) do + while not link.empty(l_in) do local p = link.receive(l_in) packet.prepend(p, self.header, HEADER_SIZE) local plength = ffi.cast(plength_ctype, p.data + LENGTH_OFFSET) @@ -199,7 +199,7 @@ function SimpleKeyedTunnel:push() l_in = self.input.encapsulated l_out = self.output.decapsulated assert(l_in and l_out) - while not link.empty(l_in) and not link.full(l_out) do + while not link.empty(l_in) do local p = link.receive(l_in) -- match next header, cookie, src/dst addresses local drop = true diff --git a/src/apps/lwaftr/generator.lua b/src/apps/lwaftr/generator.lua index ea68d497c1..258692490c 100644 --- a/src/apps/lwaftr/generator.lua +++ b/src/apps/lwaftr/generator.lua @@ -138,7 +138,7 @@ end function from_inet:pull() local o = assert(self.output.output) - while not link.full(o) do + for i=1,engine.pull_npackets do if self.max_packets then if self.tx_packets == self.max_packets then break end self.tx_packets = self.tx_packets + 1 @@ -312,7 +312,7 @@ end function from_b4:pull() local o = assert(self.output.output) - while not link.full(o) do + for i=1,engine.pull_npackets do if self.max_packets then if self.tx_packets == self.max_packets then break end self.tx_packets = self.tx_packets + 1 diff --git a/src/apps/lwaftr/ipv4_apps.lua b/src/apps/lwaftr/ipv4_apps.lua index 6401d94a1e..6ec88b7ace 100644 --- a/src/apps/lwaftr/ipv4_apps.lua +++ b/src/apps/lwaftr/ipv4_apps.lua @@ -74,6 +74,9 @@ function Reassembler:push () local input, output = self.input.input, self.output.output local errors = self.output.errors + local l2_size = self.l2_size + local ethertype_offset = self.ethertype_offset + for _=1,math.min(link.nreadable(input), link.nwritable(output)) do local pkt = receive(input) if is_ipv4(pkt) and is_fragment(pkt) then @@ -226,7 +229,7 @@ end function ICMPEcho:push() local l_in, l_out, l_reply = self.input.south, self.output.north, self.output.south - for _ = 1, math.min(link.nreadable(l_in), link.nwritable(l_out)) do + for _ = 1, link.nreadable(l_in) do local out, pkt = l_out, receive(l_in) if icmp.is_icmpv4_message(pkt, icmpv4_echo_request, 0) then @@ -263,7 +266,7 @@ function ICMPEcho:push() end l_in, l_out = self.input.north, self.output.south - for _ = 1, math.min(link.nreadable(l_in), link.nwritable(l_out)) do + for _ = 1, link.nreadable(l_in) do transmit(l_out, receive(l_in)) end end diff --git a/src/apps/lwaftr/ipv6_apps.lua b/src/apps/lwaftr/ipv6_apps.lua index e3b90afd6e..673dfa7dd6 100644 --- a/src/apps/lwaftr/ipv6_apps.lua +++ b/src/apps/lwaftr/ipv6_apps.lua @@ -227,7 +227,7 @@ end function ICMPEcho:push() local l_in, l_out, l_reply = self.input.south, self.output.north, self.output.south - for _ = 1, math.min(link.nreadable(l_in), link.nwritable(l_out)) do + for _ = 1, link.nreadable(l_in) do local out, pkt = l_out, receive(l_in) if icmp.is_icmpv6_message(pkt, icmpv6_echo_request, 0) then @@ -263,7 +263,7 @@ function ICMPEcho:push() end l_in, l_out = self.input.north, self.output.south - for _ = 1, math.min(link.nreadable(l_in), link.nwritable(l_out)) do + for _ = 1, link.nreadable(l_in) do transmit(l_out, receive(l_in)) end end diff --git a/src/apps/lwaftr/loadgen.lua b/src/apps/lwaftr/loadgen.lua index 6bb9f28e45..7d018100c3 100644 --- a/src/apps/lwaftr/loadgen.lua +++ b/src/apps/lwaftr/loadgen.lua @@ -31,7 +31,7 @@ function RateLimitedRepeater:set_rate (bit_rate) self.rate = math.max(bit_rate, 0) end -function RateLimitedRepeater:push () +function RateLimitedRepeater:pull () local i, o = self.input.input, self.output.output for _ = 1, link.nreadable(i) do local p = receive(i) @@ -53,7 +53,7 @@ function RateLimitedRepeater:push () local npackets = #self.packets if npackets > 0 and self.rate > 0 then - for _ = 1, link.nwritable(o) do + for _ = 1, engine.pull_npackets do local p = self.packets[self.index] local bits = (p.length + overhead) * 8 if bits > self.bucket_content then break end diff --git a/src/apps/pcap/pcap.lua b/src/apps/pcap/pcap.lua index 6edf919eec..930c4e09b8 100644 --- a/src/apps/pcap/pcap.lua +++ b/src/apps/pcap/pcap.lua @@ -19,7 +19,9 @@ end function PcapReader:pull () assert(self.output.output) - while not self.done and not link.full(self.output.output) do + local limit = engine.pull_npackets + while limit > 0 and not self.done do + limit = limit - 1 local data, record, extra = self.iterator() if data then local p = packet.from_string(data) diff --git a/src/apps/rate_limiter/rate_limiter.lua b/src/apps/rate_limiter/rate_limiter.lua index 34a7684661..5a1bf7e194 100644 --- a/src/apps/rate_limiter/rate_limiter.lua +++ b/src/apps/rate_limiter/rate_limiter.lua @@ -74,7 +74,7 @@ function RateLimiter:push () end - while not link.empty(i) and not link.full(o) do + while not link.empty(i) do local p = link.receive(i) local length = p.length diff --git a/src/apps/socket/raw.lua b/src/apps/socket/raw.lua index 739a6258e4..13ecb8d635 100644 --- a/src/apps/socket/raw.lua +++ b/src/apps/socket/raw.lua @@ -58,7 +58,9 @@ end function RawSocket:pull () local l = self.output.tx if l == nil then return end - while not link.full(l) and self:can_receive() do + local limit = engine.pull_npackets + while limit > 0 and self:can_receive() do + limit = limit - 1 link.transmit(l, self:receive()) end end diff --git a/src/apps/socket/unix.lua b/src/apps/socket/unix.lua index 7b906f6ab2..57d29f2b92 100644 --- a/src/apps/socket/unix.lua +++ b/src/apps/socket/unix.lua @@ -148,7 +148,9 @@ function UnixSocket:new (arg) function self:pull() local l = self.output.tx if l == nil then return end - while not link.full(l) and can_receive() do + local limit = engine.pull_npackets + while limit > 0 and can_receive() do + limit = limit - 1 local p = receive() if p then link.transmit(l, p) --link owns p now so we mustn't free it @@ -197,7 +199,7 @@ function selftest () pull = function(self) local l = self.output.tx if l == nil then return end - while not link.full(l) do + for i=1,engine.pull_npackets do local p = packet.allocate() ffi.copy(p.data, text) p.length = #text diff --git a/src/apps/solarflare/solarflare.lua b/src/apps/solarflare/solarflare.lua index b410bc7241..f1844cd0d8 100644 --- a/src/apps/solarflare/solarflare.lua +++ b/src/apps/solarflare/solarflare.lua @@ -242,19 +242,19 @@ function SolarFlareNic:pull() self.stats.pull = (self.stats.pull or 0) + 1 repeat local n_ev = self.poll_structure.n_ev + local pull_npackets = engine.pull_npackets if n_ev > 0 then for i = 0, n_ev - 1 do local event_type = self.poll_structure.events[i].generic.type - if event_type == C.EF_EVENT_TYPE_RX then + if event_type == C.EF_EVENT_TYPE_RX and pull_npackets > 0 then + pull_npackets = pull_npackets - 1 local rxpacket = self.rxpackets[self.poll_structure.events[i].rx.rq_id] rxpacket.length = self.poll_structure.events[i].rx.len self.stats.rx = (self.stats.rx or 0) + 1 - if not link.full(self.output.tx) then - link.transmit(self.output.tx, rxpacket) - else - self.stats.link_full = (self.stats.link_full or 0) + 1 - packet.free(rxpacket) - end + link.transmit(self.output.tx, rxpacket) + self.enqueue_receive(self, self.poll_structure.events[i].rx.rq_id) + elseif event_type == C.EF_EVENT_TYPE_RX and pull_npackets == 0 then + self.stats.rxdrop = (self.stats.rxdrop or 0) + 1 self.enqueue_receive(self, self.poll_structure.events[i].rx.rq_id) elseif event_type == C.EF_EVENT_TYPE_TX then local n_tx_done = self.poll_structure.unbundled_tx_request_ids[i].n_tx_done diff --git a/src/apps/tap/tap.lua b/src/apps/tap/tap.lua index 56b37fe70f..f0fb97bc98 100644 --- a/src/apps/tap/tap.lua +++ b/src/apps/tap/tap.lua @@ -45,7 +45,7 @@ end function Tap:pull () local l = self.output.output if l == nil then return end - while not link.full(l) do + for i=1,engine.pull_npackets do local p = packet.allocate() local len, err = S.read(self.sock, p.data, C.PACKET_PAYLOAD_SIZE) -- errno == EAGAIN indicates that the read would of blocked as there is no diff --git a/src/apps/test/lwaftr.lua b/src/apps/test/lwaftr.lua index fceb2d93ef..0e0e6c6638 100644 --- a/src/apps/test/lwaftr.lua +++ b/src/apps/test/lwaftr.lua @@ -255,7 +255,7 @@ function Lwaftrgen:new(arg) return setmetatable(o, {__index=Lwaftrgen}) end -function Lwaftrgen:push () +function Lwaftrgen:pull () local output = self.output.output local input = self.input.input @@ -330,8 +330,10 @@ function Lwaftrgen:push () self.bucket_content = self.bucket_content + self.rate * 1e6 * (cur_now - last_time) self.last_time = cur_now - while link.nwritable(output) > self.total_packet_count and + local limit = engine.pull_npackets + while limit > self.total_packet_count and self.total_packet_count <= self.bucket_content do + limit = limit - 1 self.bucket_content = self.bucket_content - self.total_packet_count ipv4_hdr.dst_ip = self.b4_ipv4 diff --git a/src/apps/test/synth.lua b/src/apps/test/synth.lua index 0ffc1c97fb..99a8d8dd75 100644 --- a/src/apps/test/synth.lua +++ b/src/apps/test/synth.lua @@ -33,7 +33,7 @@ end function Synth:pull () for _, o in ipairs(self.output) do - for i = 1, link.nwritable(o) do + for i = 1, engine.pull_npackets do for _, p in ipairs(self.packets) do transmit(o, packet.clone(p)) end diff --git a/src/apps/vhost/vhost_user.lua b/src/apps/vhost/vhost_user.lua index 578b22f10c..72df66cba2 100644 --- a/src/apps/vhost/vhost_user.lua +++ b/src/apps/vhost/vhost_user.lua @@ -277,6 +277,11 @@ function VhostUser:set_vring_kick (msg, fds, nfds) local idx = tonumber(bit.band(msg.u64, C.VHOST_USER_VRING_IDX_MASK)) local validfd = bit.band(msg.u64, C.VHOST_USER_VRING_NOFD_MASK) == 0 + -- Kick enables processing in vhost-user protocol + self.vhost_ready = true + -- Compile a new optimized fast-path for the vring processing + self.dev:rejit() + assert(idx < 42) if validfd then assert(nfds == 1) @@ -312,6 +317,10 @@ end function VhostUser:get_vring_base (msg) msg.state.num = self.dev:get_vring_base(msg.state.index) msg.size = ffi.sizeof("struct vhost_vring_state") + + -- get_vring_base disables vring processing in vhost-user protocol + self.vhost_ready = false + self:reply(msg) end diff --git a/src/apps/virtio_net/README.md b/src/apps/virtio_net/README.md index 490c216a47..451cf47ac3 100644 --- a/src/apps/virtio_net/README.md +++ b/src/apps/virtio_net/README.md @@ -2,12 +2,10 @@ The `VirtioNet` app implements a subset of the driver part of the [virtio-net](http://docs.oasis-open.org/virtio/virtio/v1.0/csprd04/virtio-v1.0-csprd04.html) -specification. - -With `VirtioNet` SnabbSwitch can be used as a virtual ethernet interface -by *QEMU virtual machines*. When connected via a UNIX socket, packets can -be sent to the virtual machine by transmitting them on the `rx` port and -packets send by the virtual machine will arrive on the `tx` port. +specification. It can connect to a virtio-net device from within a QEMU virtual +machine. Packets can be sent out of the virtual machine by transmitting them on +the `rx` port, and packets sent to the virtual machine will arrive on the `tx` +port. DIAGRAM: VirtioNet +-----------+ diff --git a/src/apps/virtio_net/virtio_net.lua b/src/apps/virtio_net/virtio_net.lua index 1adadc857d..ffb6dc8c76 100644 --- a/src/apps/virtio_net/virtio_net.lua +++ b/src/apps/virtio_net/virtio_net.lua @@ -18,7 +18,7 @@ local main = require("core.main") VirtioNet = {} VirtioNet.__index = VirtioNet -local receive, transmit, full, nreadable, nwritable = link.receive, link.transmit, link.full, link.nreadable, link.nwritable +local receive, transmit, nreadable = link.receive, link.transmit, link.nreadable function VirtioNet:new(args) return setmetatable({ @@ -51,7 +51,7 @@ function VirtioNet:pull() local dev = self.device local l = self.output.tx if not l then return end - local to_receive = math.min(nwritable(l), dev:can_receive()) + local to_receive = math.min(engine.pull_npackets, dev:can_receive()) for i=0, to_receive - 1 do transmit(l, dev:receive()) diff --git a/src/apps/vlan/vlan.lua b/src/apps/vlan/vlan.lua index 3ec4b6b2d3..2024e72624 100644 --- a/src/apps/vlan/vlan.lua +++ b/src/apps/vlan/vlan.lua @@ -107,18 +107,8 @@ function VlanMux:push() local noutputs = #self.output if noutputs > 0 then for name, l in pairs(self.input) do - local maxoutput = link.max - -- find out max number of packets we can put out an interface - -- this is kind of bad because we limit ourselves by the interface with - -- the fullest queue, yet packets might go out a different interface. We - -- don't know until we've looked in the packet and parsed the VLAN id. I - -- suppose we kind of get some HOLB with this :( - for _, o in ipairs(self.output) do - maxoutput = math.min(maxoutput, link.nwritable(o)) - end - if type(name) == "string" then - for _ = 1, math.min(link.nreadable(l), maxoutput) do + for _ = 1, link.nreadable(l) do local p = receive(l) local ethertype = cast("uint16_t*", p.data + o_ethernet_ethertype)[0] diff --git a/src/apps/vpn/vpws.lua b/src/apps/vpn/vpws.lua index f7c5369526..f65d37a7b1 100644 --- a/src/apps/vpn/vpws.lua +++ b/src/apps/vpn/vpws.lua @@ -72,7 +72,7 @@ function vpws:push() local l_in = self.input[port_in] local l_out = self.output[in_to_out[port_in]] assert(l_out) - while not link.full(l_out) and not link.empty(l_in) do + while not link.empty(l_in) do local p = link.receive(l_in) local datagram = self._dgram:new(p, ethernet) if port_in == 'customer' then diff --git a/src/core/app.lua b/src/core/app.lua index 30d1c0c7d8..7a6482fbee 100644 --- a/src/core/app.lua +++ b/src/core/app.lua @@ -16,6 +16,9 @@ local ffi = require("ffi") local C = ffi.C require("core.packet_h") +-- Packet per pull +pull_npackets = math.floor(link.max / 10) + -- Set to true to enable logging log = false local use_restart = false diff --git a/src/core/link.h b/src/core/link.h index f4caadaf91..add1d73bd1 100644 --- a/src/core/link.h +++ b/src/core/link.h @@ -1,6 +1,6 @@ /* Use of this source code is governed by the Apache 2.0 license; see COPYING. */ -enum { LINK_RING_SIZE = 256, +enum { LINK_RING_SIZE = 1024, LINK_MAX_PACKETS = LINK_RING_SIZE - 1 }; diff --git a/src/doc/getting-started.md b/src/doc/getting-started.md index ea8a2dbc49..aa82389cc5 100644 --- a/src/doc/getting-started.md +++ b/src/doc/getting-started.md @@ -239,18 +239,17 @@ actually exists in order to raise an exception immediately if the app was not properly connected. ``` - while not link.empty(i) and not link.full(o) do + while not link.empty(i) do self:process_packet(i, o) self.packet_counter = self.packet_counter + 1 end end ``` -Now we loop over the available packets on `i` or until `o` is full and -process each individually. This is a common Snabb idiom. The -actual logic of our app is performed by a call to the `process_packet` -method which is defined below. Note that we increment the -`packet_counter` of our instance for every packet processed. +Now we loop over the available packets on `i`and process each individually. +This is a common Snabb idiom. The actual logic of our app is performed by a +call to the `process_packet` method which is defined below. Note that we +increment the `packet_counter` of our instance for every packet processed. ``` function Sprayer:process_packet(i, o) diff --git a/src/lib/ipsec/esp.lua b/src/lib/ipsec/esp.lua index 6fa07ee2ff..a9ca071f18 100644 --- a/src/lib/ipsec/esp.lua +++ b/src/lib/ipsec/esp.lua @@ -37,8 +37,10 @@ local seq_no_t = require("lib.ipsec.seq_no_t") local lib = require("core.lib") local ffi = require("ffi") local C = ffi.C -require("lib.ipsec.track_seq_no_h") +local logger = lib.logger_new({ rate = 32, module = 'esp' }); +require("lib.ipsec.track_seq_no_h") +local window_t = ffi.typeof("uint8_t[?]") local ETHERNET_SIZE = ethernet:sizeof() local IPV6_SIZE = ipv6:sizeof() @@ -123,6 +125,8 @@ function esp_v6_decrypt:new (conf) o.CTEXT_OFFSET = ESP_SIZE + gcm.IV_SIZE o.PLAIN_OVERHEAD = PAYLOAD_OFFSET + ESP_SIZE + gcm.IV_SIZE + gcm.AUTH_SIZE o.window_size = conf.window_size or 128 + assert(o.window_size % 8 == 0, "window_size must be a multiple of 8.") + o.window = ffi.new(window_t, o.window_size / 8) return setmetatable(o, {__index=esp_v6_decrypt}) end @@ -143,10 +147,9 @@ function esp_v6_decrypt:decapsulate (p) local ctext_start = payload + self.CTEXT_OFFSET local ctext_length = length - self.PLAIN_OVERHEAD local seq_low = self.esp:seq_no() - local seq_high = C.track_seq_no(seq_low, self.seq:low(), self.seq:high(), self.window_size) - if gcm:decrypt(ctext_start, seq_low, seq_high, iv_start, ctext_start, ctext_length) then - self.seq:low(seq_low) - self.seq:high(seq_high) + local seq_high = tonumber(C.check_seq_no(seq_low, self.seq.no, self.window, self.window_size)) + if seq_high >= 0 and gcm:decrypt(ctext_start, seq_low, seq_high, iv_start, ctext_start, ctext_length) then + self.seq.no = C.track_seq_no(seq_high, seq_low, self.seq.no, self.window, self.window_size) local esp_tail_start = ctext_start + ctext_length - ESP_TAIL_SIZE self.esp_tail:new_from_mem(esp_tail_start, ESP_TAIL_SIZE) local ptext_length = ctext_length - self.esp_tail:pad_length() - ESP_TAIL_SIZE @@ -156,6 +159,15 @@ function esp_v6_decrypt:decapsulate (p) packet.resize(p, PAYLOAD_OFFSET + ptext_length) return true else + local reason = seq_high == -1 and 'replayed' or 'integrity error' + -- This is the information RFC4303 says we SHOULD log + local info = "SPI=" .. tostring(self.spi) .. ", " .. + "src_addr='" .. tostring(self.ip:ntop(self.ip:src())) .. "', " .. + "dst_addr='" .. tostring(self.ip:ntop(self.ip:dst())) .. "', " .. + "seq_low=" .. tostring(seq_low) .. ", " .. + "flow_id=" .. tostring(self.ip:flow_label()) .. ", " .. + "reason='" .. reason .. "'"; + logger:log("Rejecting packet ("..info..")") return false end end @@ -212,25 +224,66 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZ and C.memcmp(p_min.data, e_min.data, p_min.length) == 0, "integrity check failed") -- Check transmitted Sequence Number wrap around - enc.seq:low(0) - enc.seq:high(1) - dec.seq:low(2^32 - dec.window_size) - dec.seq:high(0) - local p3 = packet.clone(p) - enc:encapsulate(p3) - assert(dec:decapsulate(p3), + C.memset(dec.window, 0, dec.window_size / 8); -- clear window + enc.seq.no = 2^32 - 1 -- so next encapsulated will be seq 2^32 + dec.seq.no = 2^32 - 1 -- pretend to have seen 2^32-1 + local px = packet.clone(p) + enc:encapsulate(px) + assert(dec:decapsulate(px), "Transmitted Sequence Number wrap around failed.") - assert(dec.seq:high() == 1 and dec.seq:low() == 1, + assert(dec.seq:high() == 1 and dec.seq:low() == 0, "Lost Sequence Number synchronization.") -- Check Sequence Number exceeding window - enc.seq:low(0) - enc.seq:high(1) - dec.seq:low(dec.window_size+1) - dec.seq:high(1) - local p4 = packet.clone(p) - enc:encapsulate(p4) - assert(not dec:decapsulate(p4), + C.memset(dec.window, 0, dec.window_size / 8); -- clear window + enc.seq.no = 2^32 + dec.seq.no = 2^32 + dec.window_size + 1 + px = packet.clone(p) + enc:encapsulate(px) + assert(not dec:decapsulate(px), "Accepted out of window Sequence Number.") assert(dec.seq:high() == 1 and dec.seq:low() == dec.window_size+1, "Corrupted Sequence Number.") + -- Test anti-replay: From a set of 15 packets, first send all those + -- that have an even sequence number. Then, send all 15. Verify that + -- in the 2nd run, packets with even sequence numbers are rejected while + -- the others are not. + -- Then do the same thing again, but with offset sequence numbers so that + -- we have a 32bit wraparound in the middle. + local offset = 0 -- close to 2^32 in the 2nd iteration + for offset = 0, 2^32-7, 2^32-7 do -- duh + C.memset(dec.window, 0, dec.window_size / 8); -- clear window + dec.seq.no = offset + for i = 1+offset, 15+offset do + if (i % 2 == 0) then + enc.seq.no = i-1 -- so next seq will be i + px = packet.clone(p) + enc:encapsulate(px); + assert(dec:decapsulate(px), "rejected legitimate packet seq=" .. i) + assert(dec.seq.no == i, "Lost sequence number synchronization") + end + end + for i = 1+offset, 15+offset do + enc.seq.no = i-1 + px = packet.clone(p) + enc:encapsulate(px); + if (i % 2 == 0) then + assert(not dec:decapsulate(px), "accepted replayed packet seq=" .. i) + else + assert(dec:decapsulate(px), "rejected legitimate packet seq=" .. i) + end + end + end + -- Check that packets from way in the past/way in the future + -- (further than the biggest allowable window size) are rejected + -- This is where we ultimately want resynchronization (wrt. future packets) + C.memset(dec.window, 0, dec.window_size / 8); -- clear window + dec.seq.no = 2^34 + 42; + enc.seq.no = 2^36 + 42; + px = packet.clone(p) + enc:encapsulate(px); + assert(not dec:decapsulate(px), "accepted packet from way into the future") + enc.seq.no = 2^32 + 42; + px = packet.clone(p) + enc:encapsulate(px); + assert(not dec:decapsulate(px), "accepted packet from way into the past") end diff --git a/src/lib/ipsec/seq_no_t.lua b/src/lib/ipsec/seq_no_t.lua index a1b6f13db3..7f4e46da6c 100644 --- a/src/lib/ipsec/seq_no_t.lua +++ b/src/lib/ipsec/seq_no_t.lua @@ -6,8 +6,6 @@ local ffi = require("ffi") local seq_no_t = ffi.typeof("union { uint64_t no; uint32_t no32[2]; }") local seq_no = {} -function seq_no:full () return self.no end - local low, high if ffi.abi("le") then low = 0; high = 1 elseif ffi.abi("be") then low = 1; high = 0 end diff --git a/src/lib/ipsec/track_seq_no.c b/src/lib/ipsec/track_seq_no.c index 135946442b..78ae1c9ead 100644 --- a/src/lib/ipsec/track_seq_no.c +++ b/src/lib/ipsec/track_seq_no.c @@ -1,14 +1,104 @@ +// See https://tools.ietf.org/html/rfc4303#page-38 + +#include #include -// See https://tools.ietf.org/html/rfc4303#page-38 -// This is a only partial implementation that attempts to keep track of the -// ESN counter, but does not detect replayed packets. -uint32_t track_seq_no (uint32_t seq_no, uint32_t Tl, uint32_t Th, uint32_t W) { +/* LO32/HI32: Get lower/upper 32 bit of a 64 bit value. Should be completely + portable and endian-independent. It shouldn't be difficult for the compiler + to recognize this as division/multiplication by powers of two and hence + replace it with bit shifts, automagically in the right direction. */ +#define LO32(U64) ((uint32_t)((uint64_t)(U64) % 4294967296ull)) // 2**32 +#define HI32(U64) ((uint32_t)((uint64_t)(U64) / 4294967296ull)) // 2**32 + +/* MK64: Likewise, make a 64 bit value from two 32 bit values */ +#define MK64(L, H) ((uint64_t)(((uint32_t)(H)) * 4294967296ull + ((uint32_t)(L)))) + + +/* Set/clear the bit in our window that corresponds to sequence number `seq` */ +static inline void set_bit (bool on, uint64_t seq, + uint8_t* window, uint32_t W) { + uint32_t bitno = seq % W; + uint32_t blockno = bitno / 8; + bitno %= 8; + + /* First clear the bit, then maybe set it. This way we don't have to branch + on `on` */ + window[blockno] &= ~(1u << bitno); + window[blockno] |= (uint8_t)on << bitno; +} + +/* Get the bit in our window that corresponds to sequence number `seq` */ +static inline bool get_bit (uint64_t seq, + uint8_t *window, uint32_t W) { + uint32_t bitno = seq % W; + uint32_t blockno = bitno / 8; + bitno = bitno % 8; + + return window[blockno] & (1u << bitno); +} + +/* Advance the window so that the "head" bit corresponds to sequence + * number `seq`. Clear all bits for the new sequence numbers that are + * now considered in-window. + */ +static void advance_window (uint64_t seq, + uint64_t T, uint8_t* window, uint32_t W) { + uint64_t diff = seq - T; + + /* For advances greater than the window size, don't clear more bits than the + window has */ + if (diff > W) diff = W; + + /* Clear all bits corresponding to the sequence numbers that used to be ahead + of, but are now inside our window since we haven't seen them yet */ + while (diff--) set_bit(0, seq--, window, W); +} + + +/* Determine whether a packet with the sequence number made from + * `seq_hi` and `seq_lo` (where `seq_hi` is inferred from our window + * state) could be a legitimate packet. + * + * "Could", because we can't really tell if the received packet with + * this sequence number is in fact valid (since we haven't yet + * integrity-checked it - the sequence number may be spoofed), but we + * can give an authoritative "no" if we have already seen and accepted + * a packet with this number. + * + * If our answer is NOT "no", the caller will, provided the packet was + * valid, use track_seq_no() for us to mark the sequence number as seen. + */ +int64_t check_seq_no (uint32_t seq_lo, + uint64_t T, uint8_t *window, uint32_t W) { + uint32_t Tl = LO32(T); + uint32_t Th = HI32(T); + uint32_t seq_hi; + uint64_t seq; + if (Tl >= W - 1) { // Case A - if (seq_no >= Tl - W + 1) return Th; - else return Th + 1; + if (seq_lo >= Tl - W + 1) seq_hi = Th; + else seq_hi = Th + 1; } else { // Case B - if (seq_no >= Tl - W + 1) return Th - 1; - else return Th; + if (seq_lo >= Tl - W + 1) seq_hi = Th - 1; + else seq_hi = Th; + } + seq = MK64(seq_lo, seq_hi); + if (seq <= T && get_bit(seq, window, W)) return -1; + else return seq_hi; +} + +/* Signal that the packet received with this sequence number was + * in fact valid -- we record that we have seen it so as to prevent + * future replays of it. + */ +uint64_t track_seq_no (uint32_t seq_hi, uint32_t seq_lo, + uint64_t T, uint8_t *window, uint32_t W) { + uint64_t seq = MK64(seq_lo, seq_hi); + + if (seq > T) { + advance_window(seq, T, window, W); + T = seq; } + set_bit(1, seq, window, W); + return T; } diff --git a/src/lib/ipsec/track_seq_no.h b/src/lib/ipsec/track_seq_no.h index 8d5e5105b2..5f11c4d49e 100644 --- a/src/lib/ipsec/track_seq_no.h +++ b/src/lib/ipsec/track_seq_no.h @@ -1 +1,2 @@ -uint32_t track_seq_no (uint32_t, uint32_t, uint32_t, uint32_t); +uint64_t track_seq_no (uint32_t, uint32_t, uint64_t, uint8_t *, uint32_t); +int64_t check_seq_no (uint32_t, uint64_t, uint8_t *, uint32_t); diff --git a/src/lib/virtio/net_device.lua b/src/lib/virtio/net_device.lua index fe694721f4..10164d3a11 100644 --- a/src/lib/virtio/net_device.lua +++ b/src/lib/virtio/net_device.lua @@ -10,12 +10,11 @@ local link = require("core.link") local memory = require("core.memory") local packet = require("core.packet") local timer = require("core.timer") -local vq = require("lib.virtio.virtq_device") +local VirtioVirtq = require("lib.virtio.virtq_device") local checksum = require("lib.checksum") local ffi = require("ffi") local C = ffi.C local band = bit.band -local get_buffers = vq.VirtioVirtq.get_buffers require("lib.virtio.virtio.h") require("lib.virtio.virtio_vring_h") @@ -88,10 +87,10 @@ function VirtioNetDevice:new(owner, disable_mrg_rxbuf, disable_indirect_desc) for i = 0, max_virtq_pairs-1 do -- TXQ - o.virtq[2*i] = vq.VirtioVirtq:new() + o.virtq[2*i] = VirtioVirtq:new() o.virtq[2*i].device = o -- RXQ - o.virtq[2*i+1] = vq.VirtioVirtq:new() + o.virtq[2*i+1] = VirtioVirtq:new() o.virtq[2*i+1].device = o end @@ -129,7 +128,7 @@ function VirtioNetDevice:receive_packets_from_vm () for i = 0, self.virtq_pairs-1 do self.ring_id = 2*i+1 local virtq = self.virtq[self.ring_id] - get_buffers(virtq, 'rx', ops, self.hdr_size) + virtq:get_buffers('rx', ops, self.hdr_size) end end @@ -206,7 +205,7 @@ function VirtioNetDevice:transmit_packets_to_vm () for i = 0, self.virtq_pairs-1 do self.ring_id = 2*i local virtq = self.virtq[self.ring_id] - get_buffers(virtq, 'tx', ops, self.hdr_size) + virtq:get_buffers('tx', ops, self.hdr_size) end end @@ -271,7 +270,11 @@ function VirtioNetDevice:tx_packet_start_mrg_rxbuf(addr, len) if link.empty(l) then return end tx_p = link.receive(l) - tx_mrg_hdr.hdr.flags = validflags(tx_p.data+14, tx_p.length-14) + if band(self.features, C.VIRTIO_NET_F_CSUM) == 0 then + tx_mrg_hdr.hdr.flags = 0 + else + tx_mrg_hdr.hdr.flags = validflags(tx_p.data+14, tx_p.length-14) + end self.tx.tx_mrg_hdr[0] = tx_mrg_hdr self.tx.data_sent = 0 @@ -491,6 +494,28 @@ feature_names = { [C.VIRTIO_NET_F_MQ] = "VIRTIO_NET_F_MQ" } +-- Request fresh Just-In-Time compilation of the vring processing code. +-- +-- This should be called when the expected workload has changed +-- significantly, for example when a virtual machine loads a new +-- device driver or renegotiates features. This will cause LuaJIT to +-- generate fresh machine code for the traffic processing fast-path. +-- +-- See background motivation here: +-- https://github.com/LuaJIT/LuaJIT/issues/208#issuecomment-236423732 +function VirtioNetDevice:rejit () + local mod = "lib.virtio.virtq_device" + -- Load fresh copies of the virtq module: one for tx, one for rx. + local txvirtq = package.loaders[1](mod)(mod) + local rxvirtq = package.loaders[1](mod)(mod) + local tx_mt = {__index = txvirtq} + local rx_mt = {__index = rxvirtq} + for i = 0, max_virtq_pairs-1 do + setmetatable(self.virtq[2*i], tx_mt) + setmetatable(self.virtq[2*i+1], rx_mt) + end +end + function get_feature_names(bits) local string = "" for mask,name in pairs(feature_names) do diff --git a/src/lib/virtio/virtq_device.lua b/src/lib/virtio/virtq_device.lua index 2ac95bb35a..25f2343a41 100644 --- a/src/lib/virtio/virtq_device.lua +++ b/src/lib/virtio/virtq_device.lua @@ -3,8 +3,6 @@ -- Implements virtio virtq -module(...,package.seeall) - local lib = require("core.lib") local memory = require("core.memory") local ffi = require("ffi") @@ -111,3 +109,5 @@ function VirtioVirtq:signal_used () end end end + +return VirtioVirtq diff --git a/src/program/example_spray/sprayer.lua b/src/program/example_spray/sprayer.lua index 51140eba84..9c206bba0f 100755 --- a/src/program/example_spray/sprayer.lua +++ b/src/program/example_spray/sprayer.lua @@ -13,7 +13,7 @@ function Sprayer:push() local i = assert(self.input.input, "input port not found") local o = assert(self.output.output, "output port not found") - while not link.empty(i) and not link.full(o) do + while not link.empty(i) do self:process_packet(i, o) self.packet_counter = self.packet_counter + 1 end diff --git a/src/program/lisper/lisper.lua b/src/program/lisper/lisper.lua index 64e4f3c386..0f43e4c181 100644 --- a/src/program/lisper/lisper.lua +++ b/src/program/lisper/lisper.lua @@ -574,12 +574,9 @@ local function route_packet(p, rxname, txports) if not loc.encrypt(dp) then return end --invalid packet end end - if not link.full(tx) then - link.transmit(tx, dp) - stats.tx = stats.tx + 1 - else - packet.free(dp) - end + link.transmit(tx, dp) + stats.tx = stats.tx + 1 + packet.free(dp) end return p @@ -612,7 +609,7 @@ end function Punt:pull() local tx = self.output.tx if tx == nil then return end - while not link.full(tx) do + for i=1,engine.pull_npackets do local s = get_punt_message() if not s then break end local p = packet.allocate() diff --git a/src/program/snabbmark/snabbmark.lua b/src/program/snabbmark/snabbmark.lua index 5dad61aaff..aff47256e1 100644 --- a/src/program/snabbmark/snabbmark.lua +++ b/src/program/snabbmark/snabbmark.lua @@ -120,7 +120,7 @@ end function Source:pull() for _, o in ipairs(self.output) do - for i = 1, link.nwritable(o) do + for i = 1, engine.pull_npackets do local p = packet.allocate() ffi.copy(p.data, self.to_mac_address, 6) ffi.copy(p.data + 6, self.from_mac_address, 6) @@ -379,6 +379,8 @@ function esp (npackets, packet_size, mode, profile) for i = 1, npackets do plain = packet.clone(encapsulated) dec:decapsulate(plain) + dec.seq.no = 0 + dec.window[0] = 0 packet.free(plain) end local finish = C.get_monotonic_time() diff --git a/src/program/snabbnfv/test_env/test_env.sh b/src/program/snabbnfv/test_env/test_env.sh index e895d48ad6..e77c3c9a3a 100644 --- a/src/program/snabbnfv/test_env/test_env.sh +++ b/src/program/snabbnfv/test_env/test_env.sh @@ -1,5 +1,7 @@ #!/bin/bash +SKIPPED_CODE=43 + if [ -z "$MAC" ]; then export MAC=52:54:00:00:00: echo "Defaulting to MAC=$MAC" @@ -95,7 +97,7 @@ function qemu_log { function qemu_image { image=$assets/$1${qemu_n}.img - [ -f $image ] || cp $assets/$1.img $image + [ -f $image ] || cp $assets/$1.img $image 2> /dev/null echo $image } @@ -134,6 +136,11 @@ function launch_qemu { } function qemu { + local image=$(qemu_image "qemu") + if [ ! -f "$image" ]; then + echo "Couldn't find QEMU image: ${image}" + exit $SKIPPED_CODE + fi launch_qemu $1 $2 $3 bzImage qemu } diff --git a/src/program/snabbnfv/test_fixtures/nfvconfig/test_functions/filter.ports b/src/program/snabbnfv/test_fixtures/nfvconfig/test_functions/filter.ports index 0df5f6fe6a..2f9d7c7937 100644 --- a/src/program/snabbnfv/test_fixtures/nfvconfig/test_functions/filter.ports +++ b/src/program/snabbnfv/test_fixtures/nfvconfig/test_functions/filter.ports @@ -9,7 +9,8 @@ return { { vlan = 0, mac_address = "52:54:00:00:00:01", port_id = "B", - ingress_filter = "icmp6 or (ip6 and tcp and dst port 12345)", + -- NB: Allow iperf + ingress_filter = "icmp6 or (ip6 and tcp and dst port 12345) or arp or port 5001", gbps = nil, tunnel = nil }, diff --git a/src/scripts/snabb_bot.sh b/src/scripts/snabb_bot.sh index 548e48770f..b7109d307d 100755 --- a/src/scripts/snabb_bot.sh +++ b/src/scripts/snabb_bot.sh @@ -32,7 +32,11 @@ function init { function clean { rm -rf "$tmpdir"; } function fetch_pull_requests { - curl "https://api.github.com/repos/$REPO/pulls" > "$tmpdir/pulls" + local url="https://api.github.com/repos/$REPO/pulls?per_page=100" + if [[ -n "$CLIENT_ID" && -n "$CLIENT_SECRET" ]]; then + url="$url?client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRET}" + fi + curl -u "$GITHUB_CREDENTIALS" "$url" > "$tmpdir/pulls" } function pull_request_ids { "$JQ" ".[].number" "$tmpdir/pulls"; } @@ -69,7 +73,11 @@ function pull_request_new_p { } function clone_upstream { - git clone https://github.com/$REPO.git $(repo_path) + if [[ -n "$GITHUB_CREDENTIALS" ]]; then + git clone "https://$GITHUB_CREDENTIALS@github.com/$REPO.git" $(repo_path) + else + git clone "https://github.com/$REPO.git" $(repo_path) + fi } function dock_build { (cd src && scripts/dock.sh "(cd .. && make)"); } @@ -131,13 +139,11 @@ function dock_make { (cd src/; scripts/dock.sh make $1); } function check_for_performance_regressions { echo "Checking for performance regressions:" - local head=$(pull_request_head $1) - local target=$(pull_request_target $1) - dock_make benchmarks > $(benchmark_results $head) - for bench in $(cut -d " " -f 1 $(benchmark_results $head)); do - if grep $bench $(benchmark_results $target) >/dev/null 2>&1; then - echo $(grep $bench $(benchmark_results $target)) \ - $(grep $bench $(benchmark_results $head)) \ + dock_make benchmarks > $(benchmark_results pr) + for bench in $(cut -d " " -f 1 $(benchmark_results pr)); do + if grep $bench $(benchmark_results current) >/dev/null 2>&1; then + echo $(grep "$bench " $(benchmark_results current)) \ + $(grep "$bench " $(benchmark_results pr)) \ | awk ' BEGIN { minratio = 0.85; @@ -171,6 +177,10 @@ function check_test_suite { } function post_gist { + local url="https://api.github.com/gists" + if [[ -n "$CLIENT_ID" && -n "$CLIENT_SECRET" ]]; then + url="$url?client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRET}" + fi # Create API request body for Gist API. cat "$1" \ | "$JQ" -s -R "{public: true, files: {log: {content: .}}}" \ @@ -179,11 +189,15 @@ function post_gist { curl -X POST \ -u "$GITHUB_CREDENTIALS" \ -d @"$tmpdir/request" \ - "https://api.github.com/gists" \ + "$url" \ | "$JQ" .html_url } function post_status { id=$1; status=$2; gist=$3 + local url="https://api.github.com/repos/$REPO/statuses/$(pull_request_head $id)" + if [[ -n "$CLIENT_ID" && -n "$CLIENT_SECRET" ]]; then + url="$url?client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRET}" + fi # Create API request body for status API. cat > "$tmpdir/request" \ < /dev/null } diff --git a/src/scripts/snabb_doc.sh b/src/scripts/snabb_doc.sh index 025c4bb634..ce519f3603 100755 --- a/src/scripts/snabb_doc.sh +++ b/src/scripts/snabb_doc.sh @@ -30,7 +30,8 @@ function init { function clean { rm -rf "$tmpdir"; } function fetch_pull_requests { - curl "https://api.github.com/repos/$REPO/pulls" > "$tmpdir/pulls" + curl "https://api.github.com/repos/$REPO/pulls?per_page=100" \ + > "$tmpdir/pulls" } function pull_request_ids { "$JQ" ".[].number" "$tmpdir/pulls"; } @@ -58,7 +59,8 @@ function fetch_pr_head { } function ensure_docs_cloned { - [ -d $docdir ] || git clone git@github.com:$DOCREPO.git $docdir + [ -d $docdir ] || \ + git clone https://$GITHUB_CREDENTIALS@github.com/$DOCREPO.git $docdir mkdir -p $docdir/{sha1,tag} }