2004-09-24 Paul Jakma <paul@dishone.st>
* ospf_packet.c: (ospf_write) ifdef fragmentation support.
move actual fragmentation out to a new, similarly ifdefed, function.
(ospf_write_frags) fragmented write support, moved from previous.
diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c
index 111ee09..5952d18 100644
--- a/ospfd/ospf_packet.c
+++ b/ospfd/ospf_packet.c
@@ -507,6 +507,70 @@
iph->ip_id = ntohs(iph->ip_id);
}
+#ifdef WANT_OSPF_WRITE_FRAGMENT
+void
+ospf_write_frags (struct ospf_packet *op, struct ip *ip, struct msghdr *msg,
+ struct iovec *iov, int maxdatasize);
+{
+#define OSPF_WRITE_FRAG_SHIFT 3
+
+ assert ( op->length == stream_get_endp(op->s) );
+
+ /* we can but try.
+ *
+ * SunOS, BSD and BSD derived kernels likely will clear ip_id, as
+ * well as the IP_MF flag, making this all quite pointless.
+ *
+ * However, for a system on which IP_MF is left alone, and ip_id left
+ * alone or else which sets same ip_id for each fragment this might
+ * work, eg linux.
+ *
+ * XXX-TODO: It would be much nicer to have the kernel's use their
+ * existing fragmentation support to do this for us. Bugs/RFEs need to
+ * be raised against the various kernels.
+ */
+
+ /* set More Frag */
+ iph->ip_off |= IP_MF;
+
+ /* ip frag offset is expressed in units of 8byte words */
+ offset = maxdatasize >> OSPF_WRITE_FRAG_SHIFT;
+
+ while ( (stream_get_endp(op->s) - stream_get_getp (op->s))
+ > maxdatasize )
+ {
+ /* data length of this frag is to next offset value */
+ iov[1]->iov_len = offset << OSPF_WRITE_FRAG_SHIFT;
+ iph->ip_len = iov[1]->iov_len + sizeof (struct ip);
+ assert (iph->ip_len <= oi->ifp->mtu);
+
+ ospf_swab_iph_ton (iph);
+
+ ret = sendmsg (ospf->fd, msg, flags);
+
+ ospf_swab_iph_toh (iph);
+
+ if (ret < 0)
+ zlog_warn ("*** sendmsg in ospf_write to %s,"
+ " id %d, off %d, len %d failed with %s",
+ inet_ntoa (iph->ip_dst),
+ iph->ip_id,
+ iph->ip_off,
+ iph->ip_len,
+ strerror (errno));
+
+ iph->ip_off += offset;
+ stream_forward (op->s, iov[1]->iov_len);
+ iov[1]->iov_base = STREAM_PNT (op->s);
+ }
+
+ /* setup for final fragment */
+ iov[1]->iov_len = stream_get_endp(op->s) - stream_get_getp (op->s);
+ iph->ip_len = iov[1]->iov_len + sizeof (struct ip);
+ iph->ip_off &= (~IP_MF);
+}
+#endif /* WANT_OSPF_WRITE_FRAGMENT */
+
int
ospf_write (struct thread *thread)
{
@@ -521,10 +585,11 @@
int ret;
int flags = 0;
struct listnode *node;
+#ifdef WANT_OSPF_WRITE_FRAGMENT
static u_int16_t ipid = 0;
+#endif /* WANT_OSPF_WRITE_FRAGMENT */
u_int16_t maxdatasize, offset;
#define OSPF_WRITE_IPHL_SHIFT 2
-#define OSPF_WRITE_FRAG_SHIFT 3
ospf->t_write = NULL;
@@ -532,11 +597,13 @@
assert (node);
oi = getdata (node);
assert (oi);
-
+
+#ifdef WANT_OSPF_WRITE_FRAGMENT
/* seed ipid static with low order bits of time */
if (ipid == 0)
ipid = (time(NULL) & 0xffff);
-
+#endif /* WANT_OSPF_WRITE_FRAGMENT */
+
/* convenience - max OSPF data per packet */
maxdatasize = oi->ifp->mtu - sizeof (struct ip);
@@ -578,14 +645,15 @@
iph.ip_v = IPVERSION;
iph.ip_tos = IPTOS_PREC_INTERNETCONTROL;
iph.ip_len = (iph.ip_hl << OSPF_WRITE_IPHL_SHIFT) + op->length;
- iph.ip_id = 0;
+#ifdef WANT_OSPF_WRITE_FRAGMENT
/* XXX-MT: not thread-safe at all..
* XXX: this presumes this is only programme sending OSPF packets
* otherwise, no guarantee ipid will be unique
*/
iph.ip_id = ++ipid;
-
+#endif /* WANT_OSPF_WRITE_FRAGMENT */
+
iph.ip_off = 0;
if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
iph.ip_ttl = OSPF_VL_IP_TTL;
@@ -609,63 +677,10 @@
/* Sadly we can not rely on kernels to fragment packets because of either
* IP_HDRINCL and/or multicast destination being set.
*/
+#ifdef WANT_OSPF_WRITE_FRAGMENT
if ( op->length > maxdatasize )
- {
- assert ( op->length == stream_get_endp(op->s) );
-
- /* we can but try.
- *
- * SunOS, BSD and BSD derived kernels likely will clear ip_id, as
- * well as the IP_MF flag, making this all quite pointless.
- *
- * However, for a system on which IP_MF is left alone, and ip_id left
- * alone or else which sets same ip_id for each fragment this might
- * work, eg linux.
- *
- * XXX-TODO: It would be much nicer to have the kernel's use their
- * existing fragmentation support to do this for us. Bugs/RFEs need to
- * be raised against the various kernels.
- */
-
- /* set More Frag */
- iph.ip_off |= IP_MF;
-
- /* ip frag offset is expressed in units of 8byte words */
- offset = maxdatasize >> OSPF_WRITE_FRAG_SHIFT;
-
- while ( (stream_get_endp(op->s) - stream_get_getp (op->s))
- > maxdatasize )
- {
- /* data length of this frag is to next offset value */
- iov[1].iov_len = offset << OSPF_WRITE_FRAG_SHIFT;
- iph.ip_len = iov[1].iov_len + sizeof (struct ip);
- assert (iph.ip_len <= oi->ifp->mtu);
-
- ospf_swab_iph_ton (&iph);
-
- ret = sendmsg (ospf->fd, &msg, flags);
-
- ospf_swab_iph_toh (&iph);
-
- if (ret < 0)
- zlog_warn ("*** sendmsg in ospf_write to %s,"
- " id %d, off %d, len %d failed with %s",
- inet_ntoa (iph.ip_dst),
- iph.ip_id,
- iph.ip_off,
- iph.ip_len,
- strerror (errno));
-
- iph.ip_off += offset;
- stream_forward (op->s, iov[1].iov_len);
- iov[1].iov_base = STREAM_PNT (op->s);
- }
-
- /* setup for final fragment */
- iov[1].iov_len = stream_get_endp(op->s) - stream_get_getp (op->s);
- iph.ip_len = iov[1].iov_len + sizeof (struct ip);
- iph.ip_off &= (~IP_MF);
- }
+ ospf_write_frags (&op, &ip, &msg, &iov, maxdatasize);
+#endif /* WANT_OSPF_WRITE_FRAGMENT */
/* send final fragment (could be first) */
ospf_swab_iph_ton (&iph);