diff --git a/.ocamlformat b/.ocamlformat
index ad7af1d..3bcdafe 100644
--- a/.ocamlformat
+++ b/.ocamlformat
@@ -1,4 +1,4 @@
-version = 0.18.0
+version = 0.20.0
 profile = conventional
 break-infix = fit-or-vertical
 parse-docstrings = true
diff --git a/CHANGES.md b/CHANGES.md
index a504e88..5c4cefa 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,11 @@
+## v5.3.0 (2022-03-04)
+
+* Add `with_port_of_string` function (@dinosaure, @hannesm, #108)
+* **breaking-change** Be restrictive on `Ipaddr.of_string` (@dinosaure, @hannesm, #109)
+  Before this release, `Ipaddr.of_string` accepts remaining bytes and returns
+  a valid value such as `"127.0.0.1aaaa"` is valid. Now, `ipaddr` does not
+  accept a string with remaining bytes.
+
 ## v5.2.0 (2021-09-11)
 
 * Use Cstruct.length instead of deprecated Cstruct.len (#106, @hannesm)
diff --git a/dune-project b/dune-project
index f2566c3..2e59a02 100644
--- a/dune-project
+++ b/dune-project
@@ -1,5 +1,4 @@
 (lang dune 1.9)
 (name ipaddr)
-(version v5.2.0)
 (allow_approximate_merlin)
 (using fmt 1.1)
diff --git a/ipaddr-cstruct.opam b/ipaddr-cstruct.opam
index f7c6f2e..975ac51 100644
--- a/ipaddr-cstruct.opam
+++ b/ipaddr-cstruct.opam
@@ -1,4 +1,3 @@
-version: "5.2.0"
 opam-version: "2.0"
 maintainer: "anil@recoil.org"
 authors: ["David Sheets" "Anil Madhavapeddy" "Hugo Heuzard"]
@@ -22,4 +21,4 @@ build: [
 dev-repo: "git+https://github.com/mirage/ocaml-ipaddr.git"
 description: """
 Cstruct convertions for macaddr
-"""
\ No newline at end of file
+"""
diff --git a/ipaddr-sexp.opam b/ipaddr-sexp.opam
index 1861297..735ad08 100644
--- a/ipaddr-sexp.opam
+++ b/ipaddr-sexp.opam
@@ -1,4 +1,3 @@
-version: "5.2.0"
 opam-version: "2.0"
 maintainer: "anil@recoil.org"
 authors: ["David Sheets" "Anil Madhavapeddy" "Hugo Heuzard"]
@@ -26,4 +25,4 @@ build: [
   ["dune" "build" "-p" name "-j" jobs]
   ["dune" "runtest" "-p" name "-j" jobs] {with-test}
 ]
-dev-repo: "git+https://github.com/mirage/ocaml-ipaddr.git"
\ No newline at end of file
+dev-repo: "git+https://github.com/mirage/ocaml-ipaddr.git"
diff --git a/ipaddr.opam b/ipaddr.opam
index 1ce0343..240d9c8 100644
--- a/ipaddr.opam
+++ b/ipaddr.opam
@@ -1,4 +1,3 @@
-version: "5.2.0"
 opam-version: "2.0"
 maintainer: "anil@recoil.org"
 authors: ["David Sheets" "Anil Madhavapeddy" "Hugo Heuzard"]
@@ -41,4 +40,4 @@ build: [
   ["dune" "build" "-p" name "-j" jobs]
   ["dune" "runtest" "-p" name "-j" jobs] {with-test}
 ]
-dev-repo: "git+https://github.com/mirage/ocaml-ipaddr.git"
\ No newline at end of file
+dev-repo: "git+https://github.com/mirage/ocaml-ipaddr.git"
diff --git a/lib/ipaddr.ml b/lib/ipaddr.ml
index 640711d..5a50e02 100644
--- a/lib/ipaddr.ml
+++ b/lib/ipaddr.ml
@@ -23,7 +23,6 @@ let try_with_result fn a =
   try Ok (fn a) with Parse_error (msg, _) -> Error (`Msg ("Ipaddr: " ^ msg))
 
 let failwith_msg = function Ok x -> x | Error (`Msg m) -> failwith m
-
 let map_result v f = match v with Ok v -> Ok (f v) | Error _ as e -> e
 
 let string_of_scope = function
@@ -46,29 +45,17 @@ let scope_of_string = function
   | s -> Error (`Msg ("unknown scope: " ^ s))
 
 let pp_scope fmt s = Format.pp_print_string fmt (string_of_scope s)
-
 let ( ~| ) = Int32.of_int
-
 let ( |~ ) = Int32.to_int
-
 let ( &&& ) x y = Int32.logand x y
-
 let ( ||| ) x y = Int32.logor x y
-
 let ( <|< ) x y = Int32.shift_left x y
-
 let ( >|> ) x y = Int32.shift_right_logical x y
-
 let ( >! ) x y = x >|> y &&& 0xFF_l
-
 let ( <! ) x y = x &&& 0xFF_l <|< y
-
 let need_more x = Parse_error ("not enough data", x)
-
 let char_0 = int_of_char '0'
-
 let char_a = int_of_char 'a'
-
 let char_A = int_of_char 'A'
 
 let int_of_char c =
@@ -107,7 +94,6 @@ let parse_int base s i =
   else raise (need_more s)
 
 let parse_dec_int s i = parse_int 10 s i
-
 let parse_hex_int s i = parse_int 16 s i
 
 let expect_char s i c =
@@ -186,6 +172,20 @@ module V4 = struct
 
   let of_string s = try_with_result of_string_exn s
 
+  let with_port_of_string ~default s =
+    try
+      let len = String.length s and o = ref 0 in
+      let ipv4 = of_string_raw s o in
+      if !o < len && s.[!o] = ':' then (
+        incr o;
+        let port = parse_dec_int s o in
+        expect_end s o;
+        Ok (ipv4, port))
+      else (
+        expect_end s o;
+        Ok (ipv4, default))
+    with Parse_error (msg, _) -> Error (`Msg ("Ipaddr: " ^ msg))
+
   let to_buffer b i =
     Printf.bprintf b "%ld.%ld.%ld.%ld" (i >! 24) (i >! 16) (i >! 8) (i >! 0)
 
@@ -229,12 +229,10 @@ module V4 = struct
 
   (* Int32 *)
   let of_int32 i = i
-
   let to_int32 i = i
 
   (* Int16 *)
   let of_int16 (a, b) = ~|a <|< 16 ||| ~|b
-
   let to_int16 a = (( |~ ) (a >|> 16), ( |~ ) (a &&& 0xFF_FF_l))
 
   (* MAC *)
@@ -293,20 +291,14 @@ module V4 = struct
   (* constant *)
 
   let any = make 0 0 0 0
-
   let unspecified = make 0 0 0 0
-
   let broadcast = make 255 255 255 255
-
   let localhost = make 127 0 0 1
-
   let nodes = make 224 0 0 1
-
   let routers = make 224 0 0 2
 
   module Prefix = struct
     type addr = t
-
     type t = addr * int
 
     let compare (pre, sz) (pre', sz') =
@@ -321,7 +313,6 @@ module V4 = struct
       else 0x0_FF_FF_FF_FF_l <|< 32 - sz
 
     let prefix (pre, sz) = (pre &&& mask sz, sz)
-
     let make sz pre = (pre, sz)
 
     let network_address (pre, sz) addr =
@@ -384,29 +375,19 @@ module V4 = struct
       sz1 >= sz2 && mem pre1 (pre2, sz2)
 
     let of_addr ip = make 32 ip
-
     let global = make 0 (ip 0 0 0 0)
-
     let relative = make 8 (ip 0 0 0 0)
-
     let loopback = make 8 (ip 127 0 0 0)
-
     let link = make 16 (ip 169 254 0 0)
-
     let multicast = make 4 (ip 224 0 0 0)
-
     let multicast_org = make 14 (ip 239 192 0 0)
-
     let multicast_admin = make 16 (ip 239 255 0 0)
-
     let multicast_link = make 24 (ip 224 0 0 0)
 
     (* http://tools.ietf.org/html/rfc2365 *)
 
     let private_10 = make 8 (ip 10 0 0 0)
-
     let private_172 = make 12 (ip 172 16 0 0)
-
     let private_192 = make 16 (ip 192 168 0 0)
 
     let private_blocks =
@@ -416,11 +397,8 @@ module V4 = struct
       Int32.logor pre (Int32.logxor (mask sz) 0xFF_FF_FF_FFl)
 
     let network (pre, sz) = pre &&& mask sz
-
     let address (addr, _) = addr
-
     let bits (_, sz) = sz
-
     let netmask subnet = mask (bits subnet)
 
     let first ((_, sz) as cidr) =
@@ -447,9 +425,7 @@ module V4 = struct
     else Global
 
   let is_global i = scope i = Global
-
   let is_multicast i = Prefix.(mem i multicast)
-
   let is_private i = scope i <> Global
 
   module Set = Set.Make (struct
@@ -481,7 +457,6 @@ module B128 = struct
         logor (shift_left (of_int32 c) 32) (of_int32 d) )
 
   let of_int32 x = x
-
   let to_int32 x = x
 
   let of_int16 (a, b, c, d, e, f, g, h) =
@@ -665,6 +640,20 @@ module V6 = struct
 
   let of_string s = try_with_result of_string_exn s
 
+  let with_port_of_string ~default s =
+    let len = String.length s and o = ref 0 in
+    try
+      let ipv6 = of_string_raw s o in
+      if !o < len && s.[!o] = ':' then (
+        incr o;
+        let port = parse_dec_int s o in
+        expect_end s o;
+        Ok (ipv6, port))
+      else (
+        expect_end s o;
+        Ok (ipv6, default))
+    with Parse_error (msg, _) -> Error (`Msg ("Ipaddr: " ^ msg))
+
   (* http://tools.ietf.org/html/rfc5952 *)
   let to_buffer buf addr =
     let ((a, b, c, d, e, f, g, h) as comp) = to_int16 addr in
@@ -735,7 +724,6 @@ module V6 = struct
     of_int32 (hihi, hilo, lohi, lolo)
 
   let of_octets ?off bs = try_with_result (of_octets_exn ?off) bs
-
   let write_octets ?off i bs = try_with_result (write_octets_exn ?off i) bs
 
   let to_octets i =
@@ -829,22 +817,15 @@ module V6 = struct
   (* constant *)
 
   let unspecified = make 0 0 0 0 0 0 0 0
-
   let localhost = make 0 0 0 0 0 0 0 1
-
   let interface_nodes = make 0xff01 0 0 0 0 0 0 1
-
   let link_nodes = make 0xff02 0 0 0 0 0 0 1
-
   let interface_routers = make 0xff01 0 0 0 0 0 0 2
-
   let link_routers = make 0xff02 0 0 0 0 0 0 2
-
   let site_routers = make 0xff05 0 0 0 0 0 0 2
 
   module Prefix = struct
     type addr = t
-
     type t = addr * int
 
     let compare (pre, sz) (pre', sz') =
@@ -861,7 +842,6 @@ module V6 = struct
       V4.Prefix.(mask (sz - 0), mask (sz - 32), mask (sz - 64), mask (sz - 96))
 
     let prefix (pre, sz) = (logand pre (mask sz), sz)
-
     let make sz pre = (pre, sz)
 
     let network_address (pre, sz) addr =
@@ -927,27 +907,16 @@ module V6 = struct
       sz1 >= sz2 && mem pre1 (pre2, sz2)
 
     let of_addr ip = make 128 ip
-
     let global_unicast_001 = make 3 (ip 0x2000 0 0 0 0 0 0 0)
-
     let link = make 64 (ip 0xfe80 0 0 0 0 0 0 0)
-
     let unique_local = make 7 (ip 0xfc00 0 0 0 0 0 0 0)
-
     let multicast = make 8 (ip 0xff00 0 0 0 0 0 0 0)
-
     let ipv4_mapped = make 96 (ip 0 0 0 0 0 0xffff 0 0)
-
     let noneui64_interface = make 3 (ip 0x0000 0 0 0 0 0 0 0)
-
     let solicited_node = make 104 (ip 0xff02 0 0 0 0 1 0xff00 0)
-
     let network (pre, sz) = logand pre (mask sz)
-
     let address (addr, _) = addr
-
     let bits (_, sz) = sz
-
     let netmask subnet = mask (bits subnet)
 
     let first ((_, sz) as cidr) =
@@ -1000,9 +969,7 @@ module V6 = struct
       Prefix.(network_address link addr)
 
   let is_global i = scope i = Global
-
   let is_multicast i = Prefix.(mem i multicast)
-
   let is_private i = scope i <> Global
 
   module Set = Set.Make (struct
@@ -1019,7 +986,6 @@ module V6 = struct
 end
 
 type ('v4, 'v6) v4v6 = V4 of 'v4 | V6 of 'v6
-
 type t = (V4.t, V6.t) v4v6
 
 let compare a b =
@@ -1067,10 +1033,28 @@ let of_string_raw s offset =
           in
           raise (Parse_error (msg, s))))
 
-let of_string_exn s = of_string_raw s (ref 0)
+let of_string_exn s =
+  let o = ref 0 in
+  let x = of_string_raw s o in
+  expect_end s o;
+  x
 
 let of_string s = try_with_result of_string_exn s
 
+let with_port_of_string ~default s =
+  let len = String.length s and o = ref 0 in
+  try
+    let ipv6 = of_string_raw s o in
+    if !o < len && s.[!o] = ':' then (
+      incr o;
+      let port = parse_dec_int s o in
+      expect_end s o;
+      Ok (ipv6, port))
+    else (
+      expect_end s o;
+      Ok (ipv6, default))
+  with Parse_error (msg, _) -> Error (`Msg ("Ipaddr: " ^ msg))
+
 let v6_of_v4 v4 =
   V6.(Prefix.(network_address ipv4_mapped (of_int32 (0l, 0l, 0l, v4))))
 
@@ -1081,11 +1065,8 @@ let v4_of_v6 v6 =
   else None
 
 let to_v4 = function V4 v4 -> Some v4 | V6 v6 -> v4_of_v6 v6
-
 let to_v6 = function V4 v4 -> v6_of_v4 v4 | V6 v6 -> v6
-
 let scope = function V4 v4 -> V4.scope v4 | V6 v6 -> V6.scope v6
-
 let is_global = function V4 v4 -> V4.is_global v4 | V6 v6 -> V6.is_global v6
 
 let is_multicast = function
@@ -1126,7 +1107,6 @@ module Prefix = struct
   end
 
   type addr = t
-
   type t = (V4.Prefix.t, V6.Prefix.t) v4v6
 
   let compare a b =
@@ -1154,7 +1134,11 @@ module Prefix = struct
             in
             raise (Parse_error (msg, s))))
 
-  let of_string_exn s = of_string_raw s (ref 0)
+  let of_string_exn s =
+    let o = ref 0 in
+    let x = of_string_raw s o in
+    expect_end s o;
+    x
 
   let of_string s = try_with_result of_string_exn s
 
@@ -1167,9 +1151,7 @@ module Prefix = struct
     | None -> None
 
   let to_v4 = function V4 v4 -> Some v4 | V6 v6 -> v4_of_v6 v6
-
   let to_v6 = function V4 v4 -> v6_of_v4 v4 | V6 v6 -> v6
-
   let mem ip prefix = V6.Prefix.mem (Addr.to_v6 ip) (to_v6 prefix)
 
   let subset ~subnet ~network =
diff --git a/lib/ipaddr.mli b/lib/ipaddr.mli
index a348c16..3172ae9 100644
--- a/lib/ipaddr.mli
+++ b/lib/ipaddr.mli
@@ -18,7 +18,7 @@
 
 (** A library for manipulation of IP address representations.
 
-    {e v5.2.0 - {{:https://github.com/mirage/ocaml-ipaddr} homepage}} *)
+    {e %%VERSION%% - {{:%%PKG_HOMEPAGE%%} homepage}} *)
 
 exception Parse_error of string * string
 (** [Parse_error (err,packet)] is raised when parsing of the IP address syntax
@@ -67,6 +67,12 @@ module V4 : sig
       an unspecified value during the function call. [s] will a {!Parse_error}
       exception if it is an invalid or truncated IP address. *)
 
+  val with_port_of_string :
+    default:int -> string -> (t * int, [> `Msg of string ]) result
+  (** [with_port_of_string ~default s] is the address {!t} represented by the
+      human-readble IPv4 address [s] with a possibly port [:<port>] (otherwise,
+      we take the [default] value). *)
+
   val to_string : t -> string
   (** [to_string ipv4] is the dotted decimal string representation of [ipv4],
       i.e. [XXX.XX.X.XXX]. *)
@@ -309,9 +315,7 @@ module V4 : sig
       addresses a node. *)
 
   include Map.OrderedType with type t := t
-
   module Set : Set.S with type elt := t
-
   module Map : Map.S with type key := t
 end
 
@@ -334,6 +338,16 @@ module V6 : sig
   (** Same as [of_string_exn] but returns an option type instead of raising an
       exception. *)
 
+  val with_port_of_string :
+    default:int -> string -> (t * int, [> `Msg of string ]) result
+  (** [with_port_of_string ~default ipv6_string] is the address represented by
+      [ipv6_string] with a possibly [:<port>] (otherwise, we take the [default]
+      value). Due to the [':'] separator, the user should expand [ipv6_string]
+      to let us to consider the last [:<port>] as a port. In other words:
+
+      - [::1:8080] returns the IPv6 [::1:8080] with the [default] port
+      - [0:0:0:0:0:0:0:1:8080] returns [::1] with the port [8080]. *)
+
   val of_string_raw : string -> int ref -> t
   (** Same as [of_string_exn] but takes as an extra argument the offset into the
       string for reading. *)
@@ -580,9 +594,7 @@ module V6 : sig
       addresses a node. *)
 
   include Map.OrderedType with type t := t
-
   module Set : Set.S with type elt := t
-
   module Map : Map.S with type key := t
 end
 
@@ -616,6 +628,16 @@ val of_string_raw : string -> int ref -> t
 (** Same as [of_string_exn] but takes as an extra argument the offset into the
     string for reading. *)
 
+val with_port_of_string :
+  default:int -> string -> (t * int, [> `Msg of string ]) result
+(** [with_port_of_string ~default s] parses [s] as an IPv4 or IPv6 address with
+    a possible port seperated by a [':'] (if not, we use [default]). For IPv6,
+    due to the [':'] separator, only a full expansion of the IPv6 plus the port
+    lets us to interpret the last [:<int>] as the port. In other words:
+
+    - [::1:8080] returns the IPv6 [::1:8080] with the [default] port
+    - [0:0:0:0:0:0:0:1:8080] returns [::1] with the port [8080]. *)
+
 val v4_of_v6 : V6.t -> V4.t option
 (** [v4_of_v6 ipv6] is the IPv4 representation of the IPv6 address [ipv6]. If
     [ipv6] is not an IPv4-mapped address, None is returned. *)
@@ -734,7 +756,5 @@ module Prefix : sig
 end
 
 include Map.OrderedType with type t := t
-
 module Set : Set.S with type elt := t
-
 module Map : Map.S with type key := t
diff --git a/lib/ipaddr_sexp.ml b/lib/ipaddr_sexp.ml
index ba84124..ee32071 100644
--- a/lib/ipaddr_sexp.ml
+++ b/lib/ipaddr_sexp.ml
@@ -30,22 +30,17 @@ module V4 = struct
   type t = I.t
 
   let sexp_of_t = to_sexp I.to_string
-
   let t_of_sexp = of_sexp I.of_string
-
   let compare = I.compare
 
   module Prefix = struct
     module I = Ipaddr.V4.Prefix
 
     type addr = I.addr
-
     type t = I.t
 
     let sexp_of_t = to_sexp I.to_string
-
     let t_of_sexp = of_sexp I.of_string
-
     let compare = I.compare
   end
 end
@@ -56,22 +51,17 @@ module V6 = struct
   type t = I.t
 
   let sexp_of_t = to_sexp I.to_string
-
   let t_of_sexp = of_sexp I.of_string
-
   let compare = I.compare
 
   module Prefix = struct
     module I = Ipaddr.V6.Prefix
 
     type addr = I.addr
-
     type t = I.t
 
     let sexp_of_t = to_sexp I.to_string
-
     let t_of_sexp = of_sexp I.of_string
-
     let compare = I.compare
   end
 end
@@ -81,27 +71,21 @@ module I = Ipaddr
 type t = I.t
 
 let sexp_of_t = to_sexp I.to_string
-
 let t_of_sexp = of_sexp I.of_string
-
 let compare = I.compare
 
 type scope = I.scope
 
 let sexp_of_scope = to_sexp I.string_of_scope
-
 let scope_of_sexp = of_sexp I.scope_of_string
 
 module Prefix = struct
   module I = Ipaddr.Prefix
 
   type addr = I.addr
-
   type t = I.t
 
   let sexp_of_t = to_sexp I.to_string
-
   let t_of_sexp = of_sexp I.of_string
-
   let compare = I.compare
 end
diff --git a/lib/ipaddr_sexp.mli b/lib/ipaddr_sexp.mli
index ff29efc..95ecfff 100644
--- a/lib/ipaddr_sexp.mli
+++ b/lib/ipaddr_sexp.mli
@@ -34,42 +34,33 @@
       type t = { ip : Ipaddr.t; mac : Macaddr.t }
 
       val sexp_of_t : t -> Sexplib0.t
-
       val t_of_sexp : Sexplib0.t -> t
     ]} *)
 
 type t = Ipaddr.t
 
 val sexp_of_t : Ipaddr.t -> Sexplib0.Sexp.t
-
 val t_of_sexp : Sexplib0.Sexp.t -> Ipaddr.t
-
 val compare : Ipaddr.t -> Ipaddr.t -> int
 
 type scope = Ipaddr.scope
 
 val sexp_of_scope : Ipaddr.scope -> Sexplib0.Sexp.t
-
 val scope_of_sexp : Sexplib0.Sexp.t -> Ipaddr.scope
 
 module V4 : sig
   type t = Ipaddr.V4.t
 
   val sexp_of_t : Ipaddr.V4.t -> Sexplib0.Sexp.t
-
   val t_of_sexp : Sexplib0.Sexp.t -> Ipaddr.V4.t
-
   val compare : Ipaddr.V4.t -> Ipaddr.V4.t -> int
 
   module Prefix : sig
     type addr = Ipaddr.V4.Prefix.addr
-
     type t = Ipaddr.V4.Prefix.t
 
     val sexp_of_t : Ipaddr.V4.Prefix.t -> Sexplib0.Sexp.t
-
     val t_of_sexp : Sexplib0.Sexp.t -> Ipaddr.V4.Prefix.t
-
     val compare : Ipaddr.V4.Prefix.t -> Ipaddr.V4.Prefix.t -> int
   end
 end
@@ -78,32 +69,24 @@ module V6 : sig
   type t = Ipaddr.V6.t
 
   val sexp_of_t : Ipaddr.V6.t -> Sexplib0.Sexp.t
-
   val t_of_sexp : Sexplib0.Sexp.t -> Ipaddr.V6.t
-
   val compare : Ipaddr.V6.t -> Ipaddr.V6.t -> int
 
   module Prefix : sig
     type addr = Ipaddr.V6.Prefix.addr
-
     type t = Ipaddr.V6.Prefix.t
 
     val sexp_of_t : Ipaddr.V6.Prefix.t -> Sexplib0.Sexp.t
-
     val t_of_sexp : Sexplib0.Sexp.t -> Ipaddr.V6.Prefix.t
-
     val compare : Ipaddr.V6.Prefix.t -> Ipaddr.V6.Prefix.t -> int
   end
 end
 
 module Prefix : sig
   type addr = Ipaddr.Prefix.addr
-
   type t = Ipaddr.Prefix.t
 
   val sexp_of_t : Ipaddr.Prefix.t -> Sexplib0.Sexp.t
-
   val t_of_sexp : Sexplib0.Sexp.t -> Ipaddr.Prefix.t
-
   val compare : Ipaddr.Prefix.t -> Ipaddr.Prefix.t -> int
 end
diff --git a/lib/ipaddr_unix.ml b/lib/ipaddr_unix.ml
index c92cd9e..6bb7b1b 100644
--- a/lib/ipaddr_unix.ml
+++ b/lib/ipaddr_unix.ml
@@ -16,21 +16,16 @@
  *)
 
 let to_inet_addr t = Unix.inet_addr_of_string (Ipaddr.to_string t)
-
 let of_inet_addr t = Ipaddr.of_string_exn (Unix.string_of_inet_addr t)
 
 module V4 = struct
   let to_inet_addr t = Unix.inet_addr_of_string (Ipaddr.V4.to_string t)
-
   let of_inet_addr_exn t = Ipaddr.V4.of_string_exn (Unix.string_of_inet_addr t)
-
   let of_inet_addr t = try Some (of_inet_addr_exn t) with _ -> None
 end
 
 module V6 = struct
   let to_inet_addr t = Unix.inet_addr_of_string (Ipaddr.V6.to_string t)
-
   let of_inet_addr_exn t = Ipaddr.V6.of_string_exn (Unix.string_of_inet_addr t)
-
   let of_inet_addr t = try Some (of_inet_addr_exn t) with _ -> None
 end
diff --git a/lib/ipaddr_unix.mli b/lib/ipaddr_unix.mli
index 62d2eb5..ad0008b 100644
--- a/lib/ipaddr_unix.mli
+++ b/lib/ipaddr_unix.mli
@@ -17,7 +17,7 @@
 
 (** Convert to and from [Unix] to [Ipaddr] representations
 
-    {e v5.2.0 - {{:https://github.com/mirage/ocaml-ipaddr} homepage}} *)
+    {e %%VERSION%% - {{:%%PKG_HOMEPAGE%%} homepage}} *)
 
 val to_inet_addr : Ipaddr.t -> Unix.inet_addr
 (** [to_inet_addr ip] is the {!Unix.inet_addr} equivalent of the IPv4 or IPv6
diff --git a/lib/macaddr.ml b/lib/macaddr.ml
index 95d6e0c..2f032f0 100644
--- a/lib/macaddr.ml
+++ b/lib/macaddr.ml
@@ -89,9 +89,7 @@ let parse_sextuple s i =
 
 (* Read a MAC address colon-separated string *)
 let of_string_exn x = parse_sextuple x (ref 0)
-
 let of_string x = try_with_result of_string_exn x
-
 let chri x i = Char.code (Bytes.get x i)
 
 let to_string ?(sep = ':') x =
@@ -99,9 +97,7 @@ let to_string ?(sep = ':') x =
     sep (chri x 2) sep (chri x 3) sep (chri x 4) sep (chri x 5)
 
 let to_octets x = Bytes.to_string x
-
 let pp ppf i = Format.fprintf ppf "%s" (to_string i)
-
 let broadcast = Bytes.make 6 '\255'
 
 let make_local bytegenf =
@@ -114,7 +110,5 @@ let make_local bytegenf =
   x
 
 let get_oui x = (chri x 0 lsl 16) lor (chri x 1 lsl 8) lor chri x 2
-
 let is_local x = (chri x 0 lsr 1) land 1 = 1
-
 let is_unicast x = chri x 0 land 1 = 0
diff --git a/lib/macaddr.mli b/lib/macaddr.mli
index 0aeda15..ba9d2f0 100644
--- a/lib/macaddr.mli
+++ b/lib/macaddr.mli
@@ -16,7 +16,7 @@
 
 (** A library for manipulation of MAC address representations.
 
-    {e v5.2.0 - {{:https://github.com/mirage/ocaml-ipaddr} homepage}} *)
+    {e %%VERSION%% - {{:%%PKG_HOMEPAGE%%} homepage}} *)
 
 exception Parse_error of string * string
 (** [Parse_error (err,packet)] is raised when parsing of the MAC address syntax
diff --git a/lib/macaddr_sexp.ml b/lib/macaddr_sexp.ml
index 4bb6818..02694d1 100644
--- a/lib/macaddr_sexp.ml
+++ b/lib/macaddr_sexp.ml
@@ -27,7 +27,5 @@ let to_sexp fn t = Sexp.Atom (fn t)
 type t = Macaddr.t
 
 let sexp_of_t = to_sexp Macaddr.to_string
-
 let t_of_sexp = of_sexp Macaddr.of_string
-
 let compare = Macaddr.compare
diff --git a/lib/macaddr_sexp.mli b/lib/macaddr_sexp.mli
index 3bdf726..40c27a0 100644
--- a/lib/macaddr_sexp.mli
+++ b/lib/macaddr_sexp.mli
@@ -34,14 +34,11 @@
       type t = { ip : Ipaddr.t; mac : Macaddr.t }
 
       val sexp_of_t : t -> Sexplib0.t
-
       val t_of_sexp : Sexplib0.t -> t
     ]} *)
 
 type t = Macaddr.t
 
 val sexp_of_t : Macaddr.t -> Sexplib0.Sexp.t
-
 val t_of_sexp : Sexplib0.Sexp.t -> Macaddr.t
-
 val compare : Macaddr.t -> Macaddr.t -> int
diff --git a/lib_test/test_ipaddr.ml b/lib_test/test_ipaddr.ml
index 50557c0..b87a77f 100644
--- a/lib_test/test_ipaddr.ml
+++ b/lib_test/test_ipaddr.ml
@@ -19,7 +19,6 @@ open OUnit
 open Ipaddr
 
 let error s msg = (s, Parse_error (msg, s))
-
 let need_more s = error s "not enough data"
 
 let bad_char i s =
@@ -955,6 +954,59 @@ let test_string_raw_rt () =
       assert_equal ~msg (ts, !c) (result, cursor))
     addrs
 
+let test_with_port_of_string () =
+  let default = 8080 in
+  let addrs =
+    [
+      ("127.0.0.1", (Ipaddr.(V4 V4.localhost), default));
+      ("127.0.0.1:8080", (Ipaddr.(V4 V4.localhost), 8080));
+      ("127.0.0.1:4343", (Ipaddr.(V4 V4.localhost), 4343));
+      ("::1", (Ipaddr.(V6 V6.localhost), default));
+      ("0:0:0:0:0:0:0:1:8080", (Ipaddr.(V6 V6.localhost), 8080));
+      ("0:0:0:0:0:0:0:1:4343", (Ipaddr.(V6 V6.localhost), 4343));
+    ]
+  in
+  List.iter
+    (fun (inet_addr, result) ->
+      match Ipaddr.with_port_of_string ~default inet_addr with
+      | Ok ((V4 ipv4, port) as result') ->
+          let result'' = V4.with_port_of_string ~default inet_addr in
+          let msg =
+            Format.asprintf "%s <> %a:%d" inet_addr Ipaddr.V4.pp ipv4 port
+          in
+          assert_equal ~msg result result';
+          assert_equal ~msg (Ok (ipv4, port)) result''
+      | Ok ((V6 ipv6, port) as result') ->
+          let result'' = V6.with_port_of_string ~default inet_addr in
+          let msg =
+            Format.asprintf "%s <> %a:%d" inet_addr Ipaddr.V6.pp ipv6 port
+          in
+          assert_equal ~msg result result';
+          assert_equal ~msg (Ok (ipv6, port)) result''
+      | Error (`Msg err) ->
+          assert_failure (Format.asprintf "%s: %s" inet_addr err))
+    addrs
+
+let test_invalid_with_port_of_string () =
+  let default = 8080 in
+  let addrs =
+    [
+      "127.0.0.1:"; "127.0.0.1!8080"; "0:0:0:0:0:0:0:1!8080"; "0:0:0:0:0:0:0:1:";
+    ]
+  in
+  List.iter
+    (fun inet_addr ->
+      match
+        ( Ipaddr.with_port_of_string ~default inet_addr,
+          Ipaddr.V4.with_port_of_string ~default inet_addr,
+          Ipaddr.V4.with_port_of_string ~default inet_addr )
+      with
+      | Error _, Error _, Error _ -> ()
+      | _ ->
+          assert_failure
+            (Format.asprintf "Unexpected valid inet_addr: %S" inet_addr))
+    addrs
+
 let test_string_raw_rt_bad () =
   let error (s, c) msg c' = ((s, c), (Parse_error (msg, s), c')) in
   let addrs =
@@ -1053,9 +1105,11 @@ let suite =
          "map" >:: test_map;
          "prefix_mem" >:: test_prefix_mem;
          "prefix_subset" >:: test_prefix_subset;
+         "with_port" >:: test_with_port_of_string;
+         "invalid_with_port" >:: test_invalid_with_port_of_string;
        ]
-
 ;;
+
 let _results = run_test_tt_main Test_v4.suite in
 let _results = run_test_tt_main Test_v6.suite in
 let _results = run_test_tt_main suite in
diff --git a/lib_test/test_ipaddr_b128.ml b/lib_test/test_ipaddr_b128.ml
index 808b42c..19c9491 100644
--- a/lib_test/test_ipaddr_b128.ml
+++ b/lib_test/test_ipaddr_b128.ml
@@ -44,8 +44,7 @@ let test_shift_right () =
     (Error (`Msg "Ipaddr: unexpected argument sz (must be >= 0 and < 128)"))
     (B128.shift_right (of_string_exn "::ffff:ffff") (-8))
 
-let suite = "Test B128 module" >::: [ "shift_right" >:: test_shift_right ]
+let suite = "Test B128 module" >::: [ "shift_right" >:: test_shift_right ];;
 
-;;
 let _results = run_test_tt_main suite in
 ()
diff --git a/lib_test/test_macaddr.ml b/lib_test/test_macaddr.ml
index d7ef6a3..52fcf24 100644
--- a/lib_test/test_macaddr.ml
+++ b/lib_test/test_macaddr.ml
@@ -104,6 +104,6 @@ let suite =
          "cstruct_rt_bad" >:: test_cstruct_rt_bad;
          "make_local" >:: test_make_local;
        ]
-
 ;;
+
 run_test_tt_main suite
diff --git a/macaddr-cstruct.opam b/macaddr-cstruct.opam
index 4ef73f5..9ba75cd 100644
--- a/macaddr-cstruct.opam
+++ b/macaddr-cstruct.opam
@@ -1,4 +1,3 @@
-version: "5.2.0"
 opam-version: "2.0"
 maintainer: "anil@recoil.org"
 authors: ["David Sheets" "Anil Madhavapeddy" "Hugo Heuzard"]
@@ -22,4 +21,4 @@ build: [
 dev-repo: "git+https://github.com/mirage/ocaml-ipaddr.git"
 description: """
 Cstruct convertions for macaddr
-"""
\ No newline at end of file
+"""
diff --git a/macaddr-sexp.opam b/macaddr-sexp.opam
index a0a262f..26e6fcf 100644
--- a/macaddr-sexp.opam
+++ b/macaddr-sexp.opam
@@ -1,4 +1,3 @@
-version: "5.2.0"
 opam-version: "2.0"
 maintainer: "anil@recoil.org"
 authors: ["David Sheets" "Anil Madhavapeddy" "Hugo Heuzard"]
@@ -26,4 +25,4 @@ build: [
 dev-repo: "git+https://github.com/mirage/ocaml-ipaddr.git"
 description: """
 Sexp convertions for macaddr
-"""
\ No newline at end of file
+"""
diff --git a/macaddr.opam b/macaddr.opam
index 851ca87..13fcf31 100644
--- a/macaddr.opam
+++ b/macaddr.opam
@@ -1,4 +1,3 @@
-version: "5.2.0"
 opam-version: "2.0"
 maintainer: "anil@recoil.org"
 authors: ["David Sheets" "Anil Madhavapeddy" "Hugo Heuzard"]
@@ -30,4 +29,4 @@ Features:
  * MAC-48 (Ethernet) address support
  * `Macaddr` is a `Map.OrderedType`
  * All types have sexplib serializers/deserializers optionally via the `Macaddr_sexp` library.
- """
\ No newline at end of file
+ """